migrate code to AS7, including fucked up arduino import
arduino as a library will not build with -flto. I can't figure it out. so we waste some space compared to what platformio was building. oh well. there's still over 1K of space left for programs.
This commit is contained in:
parent
1c34e1228f
commit
70b9fe0dd6
Binary file not shown.
|
@ -0,0 +1,86 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Store xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="AtmelPackComponentManagement">
|
||||||
|
<ProjectComponents>
|
||||||
|
<ProjectComponent z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
|
||||||
|
<CApiVersion></CApiVersion>
|
||||||
|
<CBundle></CBundle>
|
||||||
|
<CClass>Device</CClass>
|
||||||
|
<CGroup>Startup</CGroup>
|
||||||
|
<CSub></CSub>
|
||||||
|
<CVariant></CVariant>
|
||||||
|
<CVendor>Atmel</CVendor>
|
||||||
|
<CVersion>1.10.0</CVersion>
|
||||||
|
<DefaultRepoPath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs</DefaultRepoPath>
|
||||||
|
<DependentComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
|
||||||
|
<Description></Description>
|
||||||
|
<Files xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\include\</AbsolutePath>
|
||||||
|
<Attribute></Attribute>
|
||||||
|
<Category>include</Category>
|
||||||
|
<Condition>C</Condition>
|
||||||
|
<FileContentHash i:nil="true" />
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>include/</Name>
|
||||||
|
<SelectString></SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\include\avr\iotn402.h</AbsolutePath>
|
||||||
|
<Attribute></Attribute>
|
||||||
|
<Category>header</Category>
|
||||||
|
<Condition>C</Condition>
|
||||||
|
<FileContentHash>g0XngzH9uwBI0kp8K6DswA==</FileContentHash>
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>include/avr/iotn402.h</Name>
|
||||||
|
<SelectString></SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\templates\library.c</AbsolutePath>
|
||||||
|
<Attribute>template</Attribute>
|
||||||
|
<Category>source</Category>
|
||||||
|
<Condition>C Lib</Condition>
|
||||||
|
<FileContentHash>VjSGq44t/3IHSL1ATPOBng==</FileContentHash>
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>templates/library.c</Name>
|
||||||
|
<SelectString>Main file (.c)</SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\templates\library.cpp</AbsolutePath>
|
||||||
|
<Attribute>template</Attribute>
|
||||||
|
<Category>source</Category>
|
||||||
|
<Condition>C Lib</Condition>
|
||||||
|
<FileContentHash>W2Pmq0e0Z2LqDTKNsfzUmA==</FileContentHash>
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>templates/library.cpp</Name>
|
||||||
|
<SelectString>Main file (.cpp)</SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\gcc\dev\attiny402</AbsolutePath>
|
||||||
|
<Attribute></Attribute>
|
||||||
|
<Category>libraryPrefix</Category>
|
||||||
|
<Condition>GCC</Condition>
|
||||||
|
<FileContentHash i:nil="true" />
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>gcc/dev/attiny402</Name>
|
||||||
|
<SelectString></SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
</Files>
|
||||||
|
<PackName>ATtiny_DFP</PackName>
|
||||||
|
<PackPath>C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATtiny_DFP/1.10.348/Atmel.ATtiny_DFP.pdsc</PackPath>
|
||||||
|
<PackVersion>1.10.348</PackVersion>
|
||||||
|
<PresentInProject>true</PresentInProject>
|
||||||
|
<ReferenceConditionId>ATtiny402</ReferenceConditionId>
|
||||||
|
<RteComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
|
||||||
|
<d4p1:string></d4p1:string>
|
||||||
|
</RteComponents>
|
||||||
|
<Status>Resolved</Status>
|
||||||
|
<VersionMode>Fixed</VersionMode>
|
||||||
|
<IsComponentInAtProject>true</IsComponentInAtProject>
|
||||||
|
</ProjectComponent>
|
||||||
|
</ProjectComponents>
|
||||||
|
</Store>
|
|
@ -0,0 +1,323 @@
|
||||||
|
<?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>{47694d30-4990-4f03-a1f7-e94eb79783f0}</ProjectGuid>
|
||||||
|
<avrdevice>ATtiny402</avrdevice>
|
||||||
|
<avrdeviceseries>none</avrdeviceseries>
|
||||||
|
<OutputType>StaticLibrary</OutputType>
|
||||||
|
<Language>CPP</Language>
|
||||||
|
<OutputFileName>lib$(MSBuildProjectName)</OutputFileName>
|
||||||
|
<OutputFileExtension>.a</OutputFileExtension>
|
||||||
|
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
|
||||||
|
<AvrGccProjectExtensions>
|
||||||
|
</AvrGccProjectExtensions>
|
||||||
|
<AssemblyName>Arduino</AssemblyName>
|
||||||
|
<Name>Arduino</Name>
|
||||||
|
<RootNamespace>Arduino</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>
|
||||||
|
<ResetRule>0</ResetRule>
|
||||||
|
<eraseonlaunchrule>0</eraseonlaunchrule>
|
||||||
|
<EraseKey />
|
||||||
|
<AsfFrameworkConfig>
|
||||||
|
<framework-data xmlns="">
|
||||||
|
<options />
|
||||||
|
<configurations />
|
||||||
|
<files />
|
||||||
|
<documentation help="" />
|
||||||
|
<offline-documentation help="" />
|
||||||
|
<dependencies>
|
||||||
|
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.52.0" />
|
||||||
|
</dependencies>
|
||||||
|
</framework-data>
|
||||||
|
</AsfFrameworkConfig>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<ToolchainSettings>
|
||||||
|
<AvrGccCpp>
|
||||||
|
<avrgcc.common.Device>-mmcu=attiny402 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\gcc\dev\attiny402"</avrgcc.common.Device>
|
||||||
|
<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\ATtiny_DFP\1.10.348\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\ATtiny_DFP\1.10.348\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.linker.libraries.Libraries>
|
||||||
|
<ListValues>
|
||||||
|
<Value>libm</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.linker.libraries.Libraries>
|
||||||
|
<avrgcccpp.assembler.general.IncludePaths>
|
||||||
|
<ListValues>
|
||||||
|
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.assembler.general.IncludePaths>
|
||||||
|
</AvrGccCpp>
|
||||||
|
</ToolchainSettings>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<ToolchainSettings>
|
||||||
|
<AvrGccCpp>
|
||||||
|
<avrgcc.common.Device>-mmcu=attiny402 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\gcc\dev\attiny402"</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>
|
||||||
|
<Value>ARDUINO=108019</Value>
|
||||||
|
<Value>ARDUINO_AVR_ATtinyxy2</Value>
|
||||||
|
<Value>ARDUINO_attinyxy2</Value>
|
||||||
|
<Value>F_CPU=8000000UL</Value>
|
||||||
|
<Value>MEGATINYCORE="2.6.10"</Value>
|
||||||
|
<Value>MEGATINYCORE_MAJOR=2UL</Value>
|
||||||
|
<Value>MEGATINYCORE_MINOR=6UL</Value>
|
||||||
|
<Value>MEGATINYCORE_PATCH=10UL</Value>
|
||||||
|
<Value>MEGATINYCORE_RELEASED=1</Value>
|
||||||
|
<Value>MILLIS_USE_TIMERA0</Value>
|
||||||
|
<Value>CLOCK_SOURCE=0</Value>
|
||||||
|
<Value>LTODISABLED</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcc.compiler.symbols.DefSymbols>
|
||||||
|
<avrgcc.compiler.directories.IncludePaths>
|
||||||
|
<ListValues>
|
||||||
|
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
<Value>../megatinycore</Value>
|
||||||
|
<Value>../variants/txy2</Value>
|
||||||
|
<Value>../arduino_libraries/tinyNeoPixel_static</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.optimization.DebugLevel>Maximum (-g3)</avrgcc.compiler.optimization.DebugLevel>
|
||||||
|
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
|
||||||
|
<avrgcc.compiler.miscellaneous.OtherFlags>-std=gnu11</avrgcc.compiler.miscellaneous.OtherFlags>
|
||||||
|
<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>
|
||||||
|
<Value>ARDUINO=108019</Value>
|
||||||
|
<Value>ARDUINO_AVR_ATtinyxy2</Value>
|
||||||
|
<Value>ARDUINO_attinyxy2</Value>
|
||||||
|
<Value>F_CPU=8000000UL</Value>
|
||||||
|
<Value>MEGATINYCORE="2.6.10"</Value>
|
||||||
|
<Value>MEGATINYCORE_MAJOR=2UL</Value>
|
||||||
|
<Value>MEGATINYCORE_MINOR=6UL</Value>
|
||||||
|
<Value>MEGATINYCORE_PATCH=10UL</Value>
|
||||||
|
<Value>MEGATINYCORE_RELEASED=1</Value>
|
||||||
|
<Value>MILLIS_USE_TIMERA0</Value>
|
||||||
|
<Value>CLOCK_SOURCE=0</Value>
|
||||||
|
<Value>TWI_MORS</Value>
|
||||||
|
<Value>LTODISABLED</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.compiler.symbols.DefSymbols>
|
||||||
|
<avrgcccpp.compiler.directories.IncludePaths>
|
||||||
|
<ListValues>
|
||||||
|
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
<Value>../megatinycore</Value>
|
||||||
|
<Value>../variants/txy2</Value>
|
||||||
|
<Value>../arduino_libraries/tinyNeoPixel_static</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.compiler.directories.IncludePaths>
|
||||||
|
<avrgcccpp.compiler.optimization.level>Optimize for size (-Os)</avrgcccpp.compiler.optimization.level>
|
||||||
|
<avrgcccpp.compiler.optimization.OtherFlags>-fno-threadsafe-statics</avrgcccpp.compiler.optimization.OtherFlags>
|
||||||
|
<avrgcccpp.compiler.optimization.PackStructureMembers>True</avrgcccpp.compiler.optimization.PackStructureMembers>
|
||||||
|
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
|
||||||
|
<avrgcccpp.compiler.optimization.DebugLevel>Maximum (-g3)</avrgcccpp.compiler.optimization.DebugLevel>
|
||||||
|
<avrgcccpp.compiler.warnings.InhibitAllWarnings>True</avrgcccpp.compiler.warnings.InhibitAllWarnings>
|
||||||
|
<avrgcccpp.compiler.miscellaneous.OtherFlags>-std=gnu++17</avrgcccpp.compiler.miscellaneous.OtherFlags>
|
||||||
|
<avrgcccpp.linker.libraries.Libraries>
|
||||||
|
<ListValues>
|
||||||
|
<Value>libm</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.linker.libraries.Libraries>
|
||||||
|
<avrgcccpp.linker.miscellaneous.LinkerFlags>-Os -fuse-linker-plugin</avrgcccpp.linker.miscellaneous.LinkerFlags>
|
||||||
|
<avrgcccpp.assembler.general.IncludePaths>
|
||||||
|
<ListValues>
|
||||||
|
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.assembler.general.IncludePaths>
|
||||||
|
<avrgcccpp.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcccpp.assembler.debugging.DebugLevel>
|
||||||
|
</AvrGccCpp>
|
||||||
|
</ToolchainSettings>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="arduino_libraries\tinyNeoPixel_static\tinyNeoPixel_Static.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="arduino_libraries\tinyNeoPixel_static\tinyNeoPixel_Static.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\abi.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\Arduino.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\asm\wiring_pulse.S">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\core_devices.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\core_parameters.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\dirty_tricks.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\errata.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\ExtraWiring.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\HardwareSerial.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\hooks.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\main.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\new.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\new.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\timers.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\Tone.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\tune_guesses.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\UART.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<None Include="megatinycore\UART.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</None>
|
||||||
|
<Compile Include="megatinycore\UART0.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\UART1.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\UART_check_pins.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\UART_constants.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\UART_private.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\WInterrupts.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\WInterrupts_PA.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\WInterrupts_PB.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\WInterrupts_PC.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\wiring.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\wiring_analog.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\wiring_digital.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\wiring_extra.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\wiring_private.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\wiring_pulse.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\wiring_shift.c">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="megatinycore\WMath.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="variants\txy2\pins_arduino.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="arduino_libraries\tinyNeoPixel_static" />
|
||||||
|
<Folder Include="megatinycore" />
|
||||||
|
<Folder Include="megatinycore\asm" />
|
||||||
|
<Folder Include="arduino_libraries" />
|
||||||
|
<Folder Include="variants" />
|
||||||
|
<Folder Include="variants\txy2" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="megatinycore\new">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,168 @@
|
||||||
|
# tinyNeoPixel_Static License
|
||||||
|
The tinyNeoPixel_Static library is derived from tinyNeoPixel, which is in turn derived from adafruitNeoPixel, which is released under the terms of the LGPL 3, unlike the rest of the core which is released under LGPL 2.1.
|
||||||
|
The license was previously in a file named "COPYING". It has been renamed for consistency and prefaced for clarity.
|
||||||
|
|
||||||
|
## GNU Lesser General Public License, Version 3
|
||||||
|
```legalese
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
|
```
|
|
@ -0,0 +1,15 @@
|
||||||
|
# tinyNeoPixel documentation is elsewhere
|
||||||
|
tinyNeoPixel is a tweaked version of adafruitNeoPixel, supporting more clock speeds and correcting some improperly specified constraints, but should be code compatible excepting the change to the class name. This core also includes tinyNeoPixel_Static, which has been modified further to reduce the flash footprint. It is recommended to read the first one first, as it gives brief summaries of the API, before and discusses supported parts and particularly common issues relating to addressable LEDs, both specific to these parts and in general (including specific hazards that they can expose the user to if used improperly.
|
||||||
|
* [Summary and changes specific to tinyNeoPixel_Static](https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/tinyNeoPixel.md) - both normal and static versions use the same documentation.
|
||||||
|
* [Full class reference from Adafruit](https://adafruit.github.io/Adafruit_NeoPixel/html/class_adafruit___neo_pixel.html)
|
||||||
|
|
||||||
|
In the event that a function listed in that class reference is not defined in tinyNeoPixel, or in the event of differences in behavior between adafruitNeoPixel and tinyNeoPixel (except as noted in the first documentation link above), that is a bug, and is likely unknown to the maintainer of megaTinyCore - please report it via github issue (preferred) or by emailing SpenceKonde@gmail.com
|
||||||
|
|
||||||
|
## Licensing
|
||||||
|
**tinyNeoPixel is LGPL 3 not LGPL 2.1**
|
||||||
|
It is derived from Adafruit's adafruitNeoPixel library, which is licenced under LGPL 3.
|
||||||
|
See [LICENSE.md for the library](LICENSE.md)
|
||||||
|
|
||||||
|
The rest of this core (except where noted) is LGPL 2.1.
|
||||||
|
|
||||||
|
This distinction is rarely relevant, but consult with a legal professional if in doubt.
|
|
@ -0,0 +1,186 @@
|
||||||
|
// This is a demonstration on how to use an input device to trigger changes on your neo pixels.
|
||||||
|
// You should wire a momentary push button to connect from ground to a digital IO pin. When you
|
||||||
|
// press the button it will change to a new pixel animation. Note that you need to press the
|
||||||
|
// button once to start the first animation!
|
||||||
|
|
||||||
|
#include <tinyNeoPixel_Static.h>
|
||||||
|
|
||||||
|
#define BUTTON_PIN PIN_PA2 // Digital IO pin connected to the button. This will be
|
||||||
|
// driven with a pull-up resistor so the switch should
|
||||||
|
// pull the pin to ground momentarily. On a high -> low
|
||||||
|
// transition the button press logic will execute.
|
||||||
|
// These pins were chosen because they are present on all parts; this sketch is used for automated testing.
|
||||||
|
// Since 8-pin parts with UPDI enabled have only 5 available IO pins (PA1, 2, 3, 6, and 7), and PA3 is the
|
||||||
|
// external clock pin (we detect and give an error when using external clock source if you try to use the
|
||||||
|
// clock pin for anything else, since that doesn't work). Serial in turn is on either PA6/7 or PA1/2 (alt) so unless we
|
||||||
|
// want to make life hard on people modifying the sketch and maybe adding serial debug logging, PA1 and PA2 are the best
|
||||||
|
// choices for the pin in this sketch as an example and automated testing sketch.
|
||||||
|
|
||||||
|
|
||||||
|
#define PIXEL_PIN PIN_PA1 // Digital IO pin connected to the NeoPixels
|
||||||
|
|
||||||
|
#define PIXEL_COUNT 16
|
||||||
|
|
||||||
|
|
||||||
|
// Since this is for the static version of the library, we need to supply the pixel array
|
||||||
|
// This saves space by eliminating use of malloc() and free(), and makes the RAM used for
|
||||||
|
// the frame buffer show up when the sketch is compiled.
|
||||||
|
|
||||||
|
byte pixels[PIXEL_COUNT * 3];
|
||||||
|
// It is strongly recommended to have one variable or #define for the number of pixels, and then
|
||||||
|
// multiply that by 3 (for RGB) or 4 (for RGBW) when declaring the pixel array, as is done here
|
||||||
|
// to avoid the need for double-entry bookkeeping.
|
||||||
|
|
||||||
|
|
||||||
|
// Parameter 1 = number of pixels in strip,
|
||||||
|
// Parameter 2 = pin number (most are valid)
|
||||||
|
// Parameter 3 = color order (NEO_RGB, NEO_GRB, etc).
|
||||||
|
// Parameter 4 = pixel buffer declared above
|
||||||
|
// Unlike the Adafruit library there's no 400 kHz option. I don't think I have ever seen 400 kHz pixels, and they may no longer be made.
|
||||||
|
// Because the optimizer's hands are tied when working with classes it would impose undue burdens on all users to support it, plus it would
|
||||||
|
// need the asm routines adapted to every supported clock speed... all for the sake of a part that seems extinct in the wild.
|
||||||
|
|
||||||
|
tinyNeoPixel strip = tinyNeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB, pixels);
|
||||||
|
|
||||||
|
bool oldState = HIGH;
|
||||||
|
int showType = 0;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
||||||
|
pinMode(PIXEL_PIN, OUTPUT);
|
||||||
|
// strip.begin();
|
||||||
|
strip.show(); // Initialize all pixels to 'off'
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Get current button state.
|
||||||
|
bool newState = digitalRead(BUTTON_PIN);
|
||||||
|
|
||||||
|
// Check if state changed from high to low (button press).
|
||||||
|
if (newState == LOW && oldState == HIGH) {
|
||||||
|
// Short delay to debounce button.
|
||||||
|
delay(20);
|
||||||
|
// Check if button is still low after debounce.
|
||||||
|
newState = digitalRead(BUTTON_PIN);
|
||||||
|
if (newState == LOW) {
|
||||||
|
showType++;
|
||||||
|
if (showType > 9) {
|
||||||
|
showType = 0;
|
||||||
|
}
|
||||||
|
startShow(showType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the last button state to the old state.
|
||||||
|
oldState = newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startShow(int i) {
|
||||||
|
switch (i) {
|
||||||
|
case 0: colorWipe(strip.Color(0, 0, 0), 50); // Black/off
|
||||||
|
break;
|
||||||
|
case 1: colorWipe(strip.Color(255, 0, 0), 50); // Red
|
||||||
|
break;
|
||||||
|
case 2: colorWipe(strip.Color(0, 255, 0), 50); // Green
|
||||||
|
break;
|
||||||
|
case 3: colorWipe(strip.Color(0, 0, 255), 50); // Blue
|
||||||
|
break;
|
||||||
|
case 4: theaterChase(strip.Color(127, 127, 127), 50); // White
|
||||||
|
break;
|
||||||
|
case 5: theaterChase(strip.Color(127, 0, 0), 50); // Red
|
||||||
|
break;
|
||||||
|
case 6: theaterChase(strip.Color(0, 0, 127), 50); // Blue
|
||||||
|
break;
|
||||||
|
case 7: rainbow(20);
|
||||||
|
break;
|
||||||
|
case 8: rainbowCycle(20);
|
||||||
|
break;
|
||||||
|
case 9: theaterChaseRainbow(50);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the dots one after the other with a color
|
||||||
|
void colorWipe(uint32_t c, uint8_t wait) {
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i++) {
|
||||||
|
strip.setPixelColor(i, c);
|
||||||
|
strip.show();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rainbow(uint8_t wait) {
|
||||||
|
uint16_t i, j;
|
||||||
|
|
||||||
|
for (j = 0; j < 256; j++) {
|
||||||
|
for (i = 0; i < strip.numPixels(); i++) {
|
||||||
|
strip.setPixelColor(i, Wheel((i + j) & 255));
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slightly different, this makes the rainbow equally distributed throughout
|
||||||
|
void rainbowCycle(uint8_t wait) {
|
||||||
|
uint16_t i, j;
|
||||||
|
|
||||||
|
for (j = 0; j < 256 * 5; j++) { // 5 cycles of all colors on wheel
|
||||||
|
for (i = 0; i < strip.numPixels(); i++) {
|
||||||
|
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theatre-style crawling lights.
|
||||||
|
void theaterChase(uint32_t c, uint8_t wait) {
|
||||||
|
for (int j = 0; j < 10; j++) { // do 10 cycles of chasing
|
||||||
|
for (int q = 0; q < 3; q++) {
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
|
||||||
|
strip.setPixelColor(i + q, c); // turn every third pixel on
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
delay(wait);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
|
||||||
|
strip.setPixelColor(i + q, 0); // turn every third pixel off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theatre-style crawling lights with rainbow effect
|
||||||
|
void theaterChaseRainbow(uint8_t wait) {
|
||||||
|
for (int j = 0; j < 256; j++) { // cycle all 256 colors in the wheel
|
||||||
|
for (int q = 0; q < 3; q++) {
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
|
||||||
|
strip.setPixelColor(i + q, Wheel((i + j) % 255)); // turn every third pixel on
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
delay(wait);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
|
||||||
|
strip.setPixelColor(i + q, 0); // turn every third pixel off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input a value 0 to 255 to get a color value.
|
||||||
|
// The colours are a transition r - g - b - back to r.
|
||||||
|
uint32_t Wheel(byte WheelPos) {
|
||||||
|
WheelPos = 255 - WheelPos;
|
||||||
|
if (WheelPos < 85) {
|
||||||
|
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
|
||||||
|
}
|
||||||
|
if (WheelPos < 170) {
|
||||||
|
WheelPos -= 85;
|
||||||
|
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
|
||||||
|
}
|
||||||
|
WheelPos -= 170;
|
||||||
|
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// NeoPixel simple sketch (c) 2013 Shae Erisson, adapted to tinyNeoPixel library by Spence Konde 2019.
|
||||||
|
// released under the GPLv3 license to match the rest of the AdaFruit NeoPixel library
|
||||||
|
|
||||||
|
#include <tinyNeoPixel_Static.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Which pin on the Arduino is connected to the NeoPixels?
|
||||||
|
#define PIN 3
|
||||||
|
|
||||||
|
// How many NeoPixels are attached to the Arduino?
|
||||||
|
#define NUMPIXELS 16
|
||||||
|
|
||||||
|
// Since this is for the static version of the library, we need to supply the pixel array
|
||||||
|
// This saves space by eliminating use of malloc() and free(), and makes the RAM used for
|
||||||
|
// the frame buffer show up when the sketch is compiled.
|
||||||
|
|
||||||
|
byte pixels[NUMPIXELS * 3];
|
||||||
|
|
||||||
|
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
|
||||||
|
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
|
||||||
|
// example for more information on possible values.
|
||||||
|
|
||||||
|
tinyNeoPixel leds = tinyNeoPixel(NUMPIXELS, PIN, NEO_GRB, pixels);
|
||||||
|
|
||||||
|
int delayval = 500; // delay for half a second
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(PIN, OUTPUT);
|
||||||
|
// with tinyNeoPixel_Static, you need to set pinMode yourself. This means you can eliminate pinMode()
|
||||||
|
// and replace with direct port writes to save a couple hundred bytes in sketch size (note that this
|
||||||
|
// savings is only present when you eliminate *all* references to pinMode).
|
||||||
|
// leds.begin() not needed on tinyNeoPixel
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
// For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one.
|
||||||
|
|
||||||
|
for (int i = 0; i < NUMPIXELS; i++) {
|
||||||
|
|
||||||
|
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
|
||||||
|
leds.setPixelColor(i, leds.Color(0, 150, 0)); // Moderately bright green color.
|
||||||
|
|
||||||
|
leds.show(); // This sends the updated pixel color to the hardware.
|
||||||
|
|
||||||
|
delay(delayval); // Delay for a period of time (in milliseconds).
|
||||||
|
}
|
||||||
|
// with tinyNeoPixel_Static, since we have the pixel array, we can also directly manipulate it - this sacrifices the correction for the pixel order, and the clarity of setColor to save a tiny amount of flash and time.
|
||||||
|
for (int i = 0; i < (NUMPIXELS * 3); i++) {
|
||||||
|
pixels[i] = 150; // set byte i of array (this is channel (i%3) of led (i/3) (respectively, i%4 and i/4 for RGBW leds)
|
||||||
|
leds.show(); // show
|
||||||
|
delay(delayval); // delay for a period of time
|
||||||
|
pixels[i] = 0; // turn off the above pixel
|
||||||
|
// result is that each pixel will cycle through each of the primary colors (green, red, blue for most LEDs) in turn, and only one LED will be on at a time.
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
#include <tinyNeoPixel_Static.h>
|
||||||
|
|
||||||
|
#define PIN 3
|
||||||
|
|
||||||
|
// Parameter 1 = number of pixels in strip
|
||||||
|
// Parameter 2 = Arduino pin number (most are valid)
|
||||||
|
// Parameter 3 = pixel type flags, add together as needed:
|
||||||
|
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
|
||||||
|
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
|
||||||
|
// Parameter 4 = array to store pixel data in
|
||||||
|
|
||||||
|
#define NUMPIXELS 60
|
||||||
|
|
||||||
|
// Since this is for the static version of the library, we need to supply the pixel array
|
||||||
|
// This saves space by eliminating use of malloc() and free(), and makes the RAM used for
|
||||||
|
// the frame buffer show up when the sketch is compiled.
|
||||||
|
|
||||||
|
byte pixels[NUMPIXELS * 3];
|
||||||
|
|
||||||
|
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
|
||||||
|
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
|
||||||
|
// example for more information on possible values. Finally, for the 4th argument we pass the array we
|
||||||
|
// defined above.
|
||||||
|
|
||||||
|
tinyNeoPixel strip = tinyNeoPixel(NUMPIXELS, PIN, NEO_GRB, pixels);
|
||||||
|
|
||||||
|
// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
|
||||||
|
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
|
||||||
|
// and minimize distance between Arduino and first pixel. Avoid connecting
|
||||||
|
// on a live circuit...if you must, connect GND first.
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(PIN, OUTPUT); // set pin output - this is not done internally by the library for Static version of library
|
||||||
|
// strip.begin(); // Static version does not use this.
|
||||||
|
strip.show(); // Initialize all pixels to 'off'
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Some example procedures showing how to display to the pixels:
|
||||||
|
colorWipe(strip.Color(255, 0, 0), 50); // Red
|
||||||
|
colorWipe(strip.Color(0, 255, 0), 50); // Green
|
||||||
|
colorWipe(strip.Color(0, 0, 255), 50); // Blue
|
||||||
|
// Send a theater pixel chase in...
|
||||||
|
theaterChase(strip.Color(127, 127, 127), 50); // White
|
||||||
|
theaterChase(strip.Color(127, 0, 0), 50); // Red
|
||||||
|
theaterChase(strip.Color(0, 0, 127), 50); // Blue
|
||||||
|
|
||||||
|
rainbow(20);
|
||||||
|
rainbowCycle(20);
|
||||||
|
theaterChaseRainbow(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the dots one after the other with a color
|
||||||
|
void colorWipe(uint32_t c, uint8_t wait) {
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i++) {
|
||||||
|
strip.setPixelColor(i, c);
|
||||||
|
strip.show();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rainbow(uint8_t wait) {
|
||||||
|
uint16_t i, j;
|
||||||
|
|
||||||
|
for (j = 0; j < 256; j++) {
|
||||||
|
for (i = 0; i < strip.numPixels(); i++) {
|
||||||
|
strip.setPixelColor(i, Wheel((i + j) & 255));
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slightly different, this makes the rainbow equally distributed throughout
|
||||||
|
void rainbowCycle(uint8_t wait) {
|
||||||
|
uint16_t i, j;
|
||||||
|
|
||||||
|
for (j = 0; j < 256 * 5; j++) { // 5 cycles of all colors on wheel
|
||||||
|
for (i = 0; i < strip.numPixels(); i++) {
|
||||||
|
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theatre-style crawling lights.
|
||||||
|
void theaterChase(uint32_t c, uint8_t wait) {
|
||||||
|
for (int j = 0; j < 10; j++) { // do 10 cycles of chasing
|
||||||
|
for (int q = 0; q < 3; q++) {
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
|
||||||
|
strip.setPixelColor(i + q, c); // turn every third pixel on
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
delay(wait);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
|
||||||
|
strip.setPixelColor(i + q, 0); // turn every third pixel off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theatre-style crawling lights with rainbow effect
|
||||||
|
void theaterChaseRainbow(uint8_t wait) {
|
||||||
|
for (int j = 0; j < 256; j++) { // cycle all 256 colors in the wheel
|
||||||
|
for (int q = 0; q < 3; q++) {
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
|
||||||
|
strip.setPixelColor(i + q, Wheel((i + j) % 255)); // turn every third pixel on
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
delay(wait);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
|
||||||
|
strip.setPixelColor(i + q, 0); // turn every third pixel off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input a value 0 to 255 to get a color value.
|
||||||
|
// The colours are a transition r - g - b - back to r.
|
||||||
|
uint32_t Wheel(byte WheelPos) {
|
||||||
|
WheelPos = 255 - WheelPos;
|
||||||
|
if (WheelPos < 85) {
|
||||||
|
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
|
||||||
|
}
|
||||||
|
if (WheelPos < 170) {
|
||||||
|
WheelPos -= 85;
|
||||||
|
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
|
||||||
|
}
|
||||||
|
WheelPos -= 170;
|
||||||
|
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
#######################################
|
||||||
|
# Syntax Coloring Map For tinyNeopixel_Static
|
||||||
|
#######################################
|
||||||
|
# Class
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
tinyNeoPixel KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
setPixelColor KEYWORD2
|
||||||
|
getPixelColor KEYWORD2
|
||||||
|
setPin KEYWORD2
|
||||||
|
getPin KEYWORD2
|
||||||
|
setBrightness KEYWORD2
|
||||||
|
getBrightness KEYWORD2
|
||||||
|
numPixels KEYWORD2
|
||||||
|
getPixels KEYWORD2
|
||||||
|
show KEYWORD2
|
||||||
|
clear KEYWORD2
|
||||||
|
fill KEYWORD2
|
||||||
|
Color KEYWORD2
|
||||||
|
ColorHSV KEYWORD2
|
||||||
|
gamma8 KEYWORD2
|
||||||
|
sine8 KEYWORD2
|
||||||
|
gamma32 KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
NEO_GRB LITERAL1
|
||||||
|
NEO_RGB LITERAL1
|
||||||
|
NEO_RBG LITERAL1
|
||||||
|
NEO_GRB LITERAL1
|
||||||
|
NEO_GBR LITERAL1
|
||||||
|
NEO_BRG LITERAL1
|
||||||
|
NEO_BGR LITERAL1
|
||||||
|
NEO_WRGB LITERAL1
|
||||||
|
NEO_WRBG LITERAL1
|
||||||
|
NEO_WGRB LITERAL1
|
||||||
|
NEO_WGBR LITERAL1
|
||||||
|
NEO_WBRG LITERAL1
|
||||||
|
NEO_WBGR LITERAL1
|
||||||
|
NEO_RWGB LITERAL1
|
||||||
|
NEO_RWBG LITERAL1
|
||||||
|
NEO_RGWB LITERAL1
|
||||||
|
NEO_RGBW LITERAL1
|
||||||
|
NEO_RBWG LITERAL1
|
||||||
|
NEO_RBGW LITERAL1
|
||||||
|
NEO_GWRB LITERAL1
|
||||||
|
NEO_GWBR LITERAL1
|
||||||
|
NEO_GRWB LITERAL1
|
||||||
|
NEO_GRBW LITERAL1
|
||||||
|
NEO_GBWR LITERAL1
|
||||||
|
NEO_GBRW LITERAL1
|
||||||
|
NEO_BWRG LITERAL1
|
||||||
|
NEO_BWGR LITERAL1
|
||||||
|
NEO_BRWG LITERAL1
|
||||||
|
NEO_BRGW LITERAL1
|
||||||
|
NEO_BGWR LITERAL1
|
||||||
|
NEO_BGRW LITERAL1
|
|
@ -0,0 +1,9 @@
|
||||||
|
name=tinyNeoPixel Static
|
||||||
|
version=2.0.7
|
||||||
|
author=Adafruit (modified by Spence Konde)
|
||||||
|
maintainer=Spence Konde <spencekonde@gmail.com>
|
||||||
|
sentence=Arduino library for controlling single-wire-based LED pixels and strip for all modern (post 2016) AVR microcontrollers, and distributed with megaTinyCore and DxCore.
|
||||||
|
paragraph=This library is closely based on the original Adafruit_NeoPixel library. It has been modified to account for the improved ST performance on the tinyAVR 0-series, tinyAVR 1-series and megaAVR 0-series, and add support for speeds from 4 MHz to 48 MHz. No specific actions to choose the port the port at any speed (enabled by ST improvements). Please refer to the documentation for more information.<br>2.0.7 - Fix critical defect in 10 and 12 MHz implementations which would output the first bit only.<br/> 2.0.6 - correct naming of labels in asm to conform with our naming policy. Add show(number), which will show up to the first (number) leds. This allows the static allocation version to light up a varying number of LEDs. 2.0.5 - correct some timing and compile issues at certain speeds. <br/> 2.0.4 - Add support for operation at speeds as low as 4 MHz. Ensure that the inline assembly is specified correctly. <br/>2.0.3 - Fix issue when millis is disabled.
|
||||||
|
category=Display
|
||||||
|
url=https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/tinyNeoPixel.md
|
||||||
|
architectures=megaavr
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,334 @@
|
||||||
|
/*--------------------------------------------------------------------
|
||||||
|
This file is part of the tinyNeoPixel library, derived from
|
||||||
|
Adafruit_NeoPixel.
|
||||||
|
|
||||||
|
NeoPixel is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of
|
||||||
|
the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
NeoPixel is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with NeoPixel. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
--------------------------------------------------------------------*/
|
||||||
|
// *INDENT-OFF* astyle hates this file
|
||||||
|
// *PAD-OFF* and destroys the lookup tables!
|
||||||
|
|
||||||
|
#ifndef TINYNEOPIXEL_H
|
||||||
|
#define TINYNEOPIXEL_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#if (__AVR_ARCH__ < 100)
|
||||||
|
#error "This version of the library only supports AVRxt parts (tinyAVR 0/1/2-series, megaAVR 0-series and the AVR DA/DB/DD parts. For tinyNeoPixel, for classic AVR, get from ATTinyCore package"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The order of primary colors in the NeoPixel data stream can vary
|
||||||
|
// among device types, manufacturers and even different revisions of
|
||||||
|
// the same item. The third parameter to the Adafruit_NeoPixel
|
||||||
|
// constructor encodes the per-pixel byte offsets of the red, green
|
||||||
|
// and blue primaries (plus white, if present) in the data stream --
|
||||||
|
// the following #defines provide an easier-to-use named version for
|
||||||
|
// each permutation. e.g. NEO_GRB indicates a NeoPixel-compatible
|
||||||
|
// device expecting three bytes per pixel, with the first byte
|
||||||
|
// containing the green value, second containing red and third
|
||||||
|
// containing blue. The in-memory representation of a chain of
|
||||||
|
// NeoPixels is the same as the data-stream order; no re-ordering of
|
||||||
|
// bytes is required when issuing data to the chain.
|
||||||
|
|
||||||
|
// Bits 5,4 of this value are the offset (0-3) from the first byte of
|
||||||
|
// a pixel to the location of the red color byte. Bits 3,2 are the
|
||||||
|
// green offset and 1,0 are the blue offset. If it is an RGBW-type
|
||||||
|
// device (supporting a white primary in addition to R,G,B), bits 7,6
|
||||||
|
// are the offset to the white byte...otherwise, bits 7,6 are set to
|
||||||
|
// the same value as 5,4 (red) to indicate an RGB (not RGBW) device.
|
||||||
|
// i.e. binary representation:
|
||||||
|
// 0bWWRRGGBB for RGBW devices
|
||||||
|
// 0bRRRRGGBB for RGB
|
||||||
|
|
||||||
|
// RGB NeoPixel permutations; white and red offsets are always same
|
||||||
|
// Offset: W R G B
|
||||||
|
#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2))
|
||||||
|
#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1))
|
||||||
|
#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2))
|
||||||
|
#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1))
|
||||||
|
#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0))
|
||||||
|
#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0))
|
||||||
|
|
||||||
|
// RGBW NeoPixel permutations; all 4 offsets are distinct
|
||||||
|
// Offset: W R G B
|
||||||
|
#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3))
|
||||||
|
#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2))
|
||||||
|
#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3))
|
||||||
|
#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2))
|
||||||
|
#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1))
|
||||||
|
#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1))
|
||||||
|
|
||||||
|
#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3))
|
||||||
|
#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2))
|
||||||
|
#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3))
|
||||||
|
#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2))
|
||||||
|
#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1))
|
||||||
|
#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1))
|
||||||
|
|
||||||
|
#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3))
|
||||||
|
#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2))
|
||||||
|
#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3))
|
||||||
|
#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2))
|
||||||
|
#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1))
|
||||||
|
#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1))
|
||||||
|
|
||||||
|
#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0))
|
||||||
|
#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0))
|
||||||
|
#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0))
|
||||||
|
#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0))
|
||||||
|
#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0))
|
||||||
|
#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0))
|
||||||
|
|
||||||
|
#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission
|
||||||
|
|
||||||
|
// 400 kHz neopixels are virtually absent from the market today
|
||||||
|
// They are not supported.
|
||||||
|
|
||||||
|
// These two tables are declared outside the Adafruit_NeoPixel class
|
||||||
|
// because some boards may require oldschool compilers that don't
|
||||||
|
// handle the C++11 constexpr keyword.
|
||||||
|
|
||||||
|
/* A pre-calculated 8-bit sine look-up table stored in flash for use
|
||||||
|
with the sine8() function. This is apparently of use in some animation
|
||||||
|
algorithms. If __AVR_ARCH__==103, then all of the flash is memory
|
||||||
|
mapped, and we can simply declare it const, access it like a
|
||||||
|
normal variable, and it won't be copied to RAM.
|
||||||
|
|
||||||
|
AVRxt devices with too much flash for all of it to be mapped
|
||||||
|
which includes the AVR64Dx and AVR128Dx parts. DxCore defines a
|
||||||
|
.section for the area of PROGMEM that is mapped by default, and
|
||||||
|
a PROGMEM_MAPPED macro. A variable declared const PROGMEM_MAPPED can
|
||||||
|
be accessed normally, but will be stored in the flash and not copied to RAM.
|
||||||
|
|
||||||
|
Finally, if neither of those are an option - it gets declared with PROGMEM
|
||||||
|
|
||||||
|
|
||||||
|
Copy & paste this snippet into a Python REPL to regenerate:
|
||||||
|
import math
|
||||||
|
for x in range(256):
|
||||||
|
print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))),
|
||||||
|
if x&15 == 15: print
|
||||||
|
*/
|
||||||
|
#if (__AVR_ARCH__==103)
|
||||||
|
// All out flash is mapped - yay!
|
||||||
|
static const uint8_t _NeoPixelSineTable[256] = {
|
||||||
|
#elif defined(PROGMEM_MAPPED)
|
||||||
|
// Some of it is - but we can put stuff there - yay!
|
||||||
|
static const uint8_t PROGMEM_MAPPED _NeoPixelSineTable[256] = {
|
||||||
|
#else
|
||||||
|
// Back to progmem...
|
||||||
|
static const uint8_t PROGMEM _NeoPixelSineTable[256] = {
|
||||||
|
#endif
|
||||||
|
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
|
||||||
|
176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
|
||||||
|
218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
|
||||||
|
245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
|
||||||
|
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
|
||||||
|
245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
|
||||||
|
218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
|
||||||
|
176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
|
||||||
|
128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
|
||||||
|
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
|
||||||
|
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
|
||||||
|
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
|
||||||
|
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
|
||||||
|
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
|
||||||
|
79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124};
|
||||||
|
|
||||||
|
/* Similar to above, but for an 8-bit gamma-correction table.
|
||||||
|
Copy & paste this snippet into a Python REPL to regenerate:
|
||||||
|
import math
|
||||||
|
gamma=2.6
|
||||||
|
for x in range(256):
|
||||||
|
print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))),
|
||||||
|
if x&15 == 15: print
|
||||||
|
*/
|
||||||
|
#if (__AVR_ARCH__==103)
|
||||||
|
// All our flash is mapped - yay!
|
||||||
|
static const uint8_t _NeoPixelGammaTable[256] = {
|
||||||
|
#elif defined(PROGMEM_MAPPED)
|
||||||
|
// Some of it is - but we can put stuff there - yay!
|
||||||
|
static const uint8_t PROGMEM_MAPPED _NeoPixelGammaTable[256] = {
|
||||||
|
#else
|
||||||
|
// Back to progmem...
|
||||||
|
static const uint8_t PROGMEM _NeoPixelGammaTable[256] = {
|
||||||
|
#endif
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
|
||||||
|
3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
|
||||||
|
7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12,
|
||||||
|
13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20,
|
||||||
|
20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
|
||||||
|
30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
|
||||||
|
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
|
||||||
|
58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75,
|
||||||
|
76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96,
|
||||||
|
97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120,
|
||||||
|
122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148,
|
||||||
|
150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,
|
||||||
|
182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215,
|
||||||
|
218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255};
|
||||||
|
|
||||||
|
|
||||||
|
typedef uint8_t neoPixelType;
|
||||||
|
|
||||||
|
class tinyNeoPixel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Constructor: number of LEDs, pin number, LED type
|
||||||
|
tinyNeoPixel(uint16_t n, uint8_t p, neoPixelType t, uint8_t *pxl);
|
||||||
|
~tinyNeoPixel();
|
||||||
|
|
||||||
|
void
|
||||||
|
show(void),
|
||||||
|
#if (PROGMEM_SIZE > 4096UL)
|
||||||
|
show(uint16_t leds),
|
||||||
|
#endif
|
||||||
|
setPin(uint8_t p),
|
||||||
|
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b),
|
||||||
|
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w),
|
||||||
|
setPixelColor(uint16_t n, uint32_t c),
|
||||||
|
fill(uint32_t c = 0, uint16_t first = 0, uint16_t count = 0),
|
||||||
|
setBrightness(uint8_t b),
|
||||||
|
clear(),
|
||||||
|
updateLatch(uint16_t latchtime = 50);
|
||||||
|
uint8_t
|
||||||
|
*getPixels(void) const,
|
||||||
|
getBrightness(void) const;
|
||||||
|
uint16_t
|
||||||
|
numPixels(void) const;
|
||||||
|
uint32_t
|
||||||
|
getPixelColor(uint16_t n) const;
|
||||||
|
uint8_t getPin(void) { return pin; }
|
||||||
|
void begin(void) {return;}
|
||||||
|
/*!
|
||||||
|
@brief An 8-bit integer sine wave function, not directly compatible
|
||||||
|
with standard trigonometric units like radians or degrees.
|
||||||
|
@param x Input angle, 0-255; 256 would loop back to zero, completing
|
||||||
|
the circle (equivalent to 360 degrees or 2 pi radians).
|
||||||
|
One can therefore use an unsigned 8-bit variable and simply
|
||||||
|
add or subtract, allowing it to overflow/underflow and it
|
||||||
|
still does the expected contiguous thing.
|
||||||
|
@return Sine result, 0 to 255, or -128 to +127 if type-converted to
|
||||||
|
a signed int8_t, but you'll most likely want unsigned as this
|
||||||
|
output is often used for pixel brightness in animation effects.
|
||||||
|
*/
|
||||||
|
static uint8_t sine8(uint8_t x) { // 0-255 in, 0-255 out
|
||||||
|
#if (__AVR_ARCH__ == 103 || defined(PROGMEM_MAPPED))
|
||||||
|
return _NeoPixelSineTable[x];
|
||||||
|
#else // We had to put it in PROGMEM, and that's how we get it out
|
||||||
|
return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief An 8-bit gamma-correction function for basic pixel brightness
|
||||||
|
adjustment. Makes color transitions appear more perceptially
|
||||||
|
correct.
|
||||||
|
@param x Input brightness, 0 (minimum or off/black) to 255 (maximum).
|
||||||
|
@return Gamma-adjusted brightness, can then be passed to one of the
|
||||||
|
setPixelColor() functions. This uses a fixed gamma correction
|
||||||
|
exponent of 2.6, which seems reasonably okay for average
|
||||||
|
NeoPixels in average tasks. If you need finer control you'll
|
||||||
|
need to provide your own gamma-correction function instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint8_t gamma8(uint8_t x) {
|
||||||
|
#if (__AVR_ARCH__ == 103 || defined(PROGMEM_MAPPED))
|
||||||
|
return _NeoPixelGammaTable[x];
|
||||||
|
#else
|
||||||
|
return pgm_read_byte(&_NeoPixelGammaTable[x]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief Convert separate red, green and blue values into a single
|
||||||
|
"packed" 32-bit RGB color.
|
||||||
|
@param r Red brightness, 0 to 255.
|
||||||
|
@param g Green brightness, 0 to 255.
|
||||||
|
@param b Blue brightness, 0 to 255.
|
||||||
|
@return 32-bit packed RGB value, which can then be assigned to a
|
||||||
|
variable for later use or passed to the setPixelColor()
|
||||||
|
function. Packed RGB format is predictable, regardless of
|
||||||
|
LED strand color order.
|
||||||
|
*/
|
||||||
|
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief Convert separate red, green, blue and white values into a
|
||||||
|
single "packed" 32-bit WRGB color.
|
||||||
|
@param r Red brightness, 0 to 255.
|
||||||
|
@param g Green brightness, 0 to 255.
|
||||||
|
@param b Blue brightness, 0 to 255.
|
||||||
|
@param w White brightness, 0 to 255.
|
||||||
|
@return 32-bit packed WRGB value, which can then be assigned to a
|
||||||
|
variable for later use or passed to the setPixelColor()
|
||||||
|
function. Packed WRGB format is predictable, regardless of
|
||||||
|
LED strand color order.
|
||||||
|
*/
|
||||||
|
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||||
|
return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||||
|
}
|
||||||
|
static uint32_t ColorHSV(uint16_t hue, uint8_t sat = 255, uint8_t val = 255);
|
||||||
|
/*!
|
||||||
|
@brief A gamma-correction function for 32-bit packed RGB or WRGB
|
||||||
|
colors. Makes color transitions appear more perceptially
|
||||||
|
correct.
|
||||||
|
@param x 32-bit packed RGB or WRGB color.
|
||||||
|
@return Gamma-adjusted packed color, can then be passed in one of the
|
||||||
|
setPixelColor() functions. Like gamma8(), this uses a fixed
|
||||||
|
gamma correction exponent of 2.6, which seems reasonably okay
|
||||||
|
for average NeoPixels in average tasks. If you need finer
|
||||||
|
control you'll need to provide your own gamma-correction
|
||||||
|
function instead.
|
||||||
|
*/
|
||||||
|
static uint32_t gamma32(uint32_t x);
|
||||||
|
|
||||||
|
#if (!defined(MILLIS_USE_TIMERNONE) && !defined(MILLIS_USE_TIMERRTC) && !defined(MILLIS_USE_TIMERRTC_XTAL) && !defined(MILLIS_USE_TIMERRTC_XOSC))
|
||||||
|
inline bool canShow(void) { return (micros() - endTime) >= (uint32_t) latchTime; }
|
||||||
|
#else
|
||||||
|
inline bool canShow(void) {return 1;} // we don't have micros here;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
uint16_t
|
||||||
|
numLEDs, // Number of RGB LEDs in strip
|
||||||
|
numBytes, // Size of 'pixels' buffer below (3 or 4 bytes/pixel)
|
||||||
|
latchTime; // Latch waiting period in us varies from 6 (contrary
|
||||||
|
// to datasheet) for original 2812's, all the way to 250 us.
|
||||||
|
// 50us is what the originals claim. Clones copied that, and some made it even longer.
|
||||||
|
int8_t
|
||||||
|
pin; // Output pin number (-1 if not yet set)
|
||||||
|
uint8_t
|
||||||
|
brightness,
|
||||||
|
*pixels, // Holds LED color values (3 or 4 bytes each)
|
||||||
|
rOffset, // Index of red byte within each 3- or 4-byte pixel
|
||||||
|
gOffset, // Index of green byte
|
||||||
|
bOffset, // Index of blue byte
|
||||||
|
wOffset; // Index of white byte (same as rOffset if no white)
|
||||||
|
uint32_t
|
||||||
|
endTime; // Latch timing reference
|
||||||
|
volatile uint8_t
|
||||||
|
*port; // Output PORT register
|
||||||
|
uint8_t
|
||||||
|
pinMask; // Output PORT bitmask
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TINYNEOPIXEL_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
||||||
|
/*placeholder*/
|
|
@ -0,0 +1,473 @@
|
||||||
|
// Many cores use HardwareSerial.h and HardwareSerial. However, the core from which this was derived names them differently.
|
||||||
|
// This only rarely a problem - but it causes problems when libraries want to #include HardwareSerial.h and expect it to provide a class of that name, instead of UartClass. Soon after this workaround, it was realized that there was an extra layer in the class hierarchy forcing multiple virtual functions and explained why serial was a space hog
|
||||||
|
|
||||||
|
/* UART.h - Hardware serial library, main header.
|
||||||
|
* This library is free software released under LGPL 2.1.
|
||||||
|
* See License.md for more information.
|
||||||
|
* This file is part of DxCore and/or megaTinyCore
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 Nicholas Zambetti, Modified by
|
||||||
|
* 11/23/2006 David A. Mellis, 9/20/2010 Mark Sproul,
|
||||||
|
* 8/24/2012 Alarus, 12/3/2013 Matthijs Kooijman
|
||||||
|
* Others (unknown) 2013-2017, 2017-2022 Spence Konde
|
||||||
|
* and 2021-2022 MX682X
|
||||||
|
*
|
||||||
|
* Modified 28 September 2010 by Mark Sproul
|
||||||
|
* Modified 14 August 2012 by Alarus
|
||||||
|
* Modified 3 December 2013 by Matthijs Kooijman
|
||||||
|
* Modified by SOMEONE around 2016-2017; hardware serial didn't port itself to the megaAVR 0-series.
|
||||||
|
* Modified 2017-2021 by Spence Konde for megaTinyCore and DxCore.
|
||||||
|
* Modified late 2021 by Spence Konde and MX682X for DxCore
|
||||||
|
* 12/26/21: Correct bug introduced in my ASM macros. -Spence
|
||||||
|
* 12/30/21: Clean up tests for conditional compilation
|
||||||
|
tests for defined(USE_ASM_*) removed UART.h tests that test both defined and the value of
|
||||||
|
USE_ASM_* macros. We check if they're defined and define them if they're not defined already
|
||||||
|
so whenever UART.h has been loaded, those three macros are defined as either 1, or wharever
|
||||||
|
value the user overode them with, likely 0. Also high byte of UART address always 0x08, so replace
|
||||||
|
2-clock ldd with 1 clock ldi. - Spence
|
||||||
|
* 03/12/23: Correct bug in TxD1' and XCK1'
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "core_devices.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "api/Stream.h"
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
#include "UART_constants.h"
|
||||||
|
#include "UART_check_pins.h"
|
||||||
|
|
||||||
|
// No UART_swap.h on megaTinyCore - there isn't enough to put in separate file.
|
||||||
|
|
||||||
|
/* Define constants and variables for buffering incoming serial data. We're
|
||||||
|
* using a ring buffer, in which head is the index of the location to which
|
||||||
|
* to write the next incoming character and tail is the index of the
|
||||||
|
* location from which to read.
|
||||||
|
* NOTE: a "power of 2" buffer size is **REQUIRED** - the compiler
|
||||||
|
* was missing optimizations, and there's no particular reason to have
|
||||||
|
* a weird sized buffer, and several reasons not to.
|
||||||
|
*
|
||||||
|
* More than 256b buffers imposes a considerable performance penalty,
|
||||||
|
* - one large enough to obliviate the entire purpose, because of the
|
||||||
|
* need to make the access to the current index atomic. This atomic
|
||||||
|
* block is costly - it's a macro for cli and sei
|
||||||
|
* implemented in inline assembly, which sounds fast. But the optimizer
|
||||||
|
* can reorder instructions *and isn't smart enough not to here* without the
|
||||||
|
* ": memory" clobber to create a memory barrier. This ensures that it
|
||||||
|
* is atomic, but significantly hurts performance. (theoretical worst case
|
||||||
|
* is 94 clocks, real-world is usually far less, but I'll only say "less"
|
||||||
|
* The functions in question have considerable register pressure). But,
|
||||||
|
* it unquestionably would impact USART performance at high speeds.
|
||||||
|
*
|
||||||
|
* * The USE_ASM_* options can be disabled by defining them as 0 either in variant pins_arduino.h
|
||||||
|
* The buffer sizes can be overridden in by defining SERIAL_TX_BUFFER either in variant file (
|
||||||
|
* as defines in pins_arduino.h) or boards.txt as (By passing them as extra flags).
|
||||||
|
* Note that buffer sizes must be powers of 2 only no matter how you override it.
|
||||||
|
* The alternative is doing division to get the modulo every time instead of a single clock bitwise and, which further lowers the
|
||||||
|
* maximum RX speed (which, without the ASM implementation, is already lower than what the receiver is capable of).
|
||||||
|
* The defaults below are only used if the relevant macro isn't already defined.
|
||||||
|
* Since the USE_ASM_* = 1 option is apparently working, we do not recommend disabling it, as it will waste flash and hurt performance.
|
||||||
|
*
|
||||||
|
* Flash versus RAM table
|
||||||
|
* | | modern tinyAVR series parts | Other modern parts |
|
||||||
|
* | Flash | 0-series | 1-series | 2-series | mega | All Dx | EA | EB |
|
||||||
|
* |-------|----------|----------|----------|------|--------|------|------|
|
||||||
|
* | 2048 | 128 | 128 | - | - | - | - | - |
|
||||||
|
* | 4096 | 256 | 256 | 512 | - | - | - | - |
|
||||||
|
* | 8192 | 512 | 512 | 1024 | 1024 | - | 1024 | 1024 |
|
||||||
|
* | 16384 | 1024 | 2048 | 2048 | 2048 | 2048 | 2048 | 2048 |
|
||||||
|
* | 32768 | - | 2048 | 3072 | 4096 | 4096 | 4096 | 3072 |
|
||||||
|
* | 49152 | - | - | - | 6120 | - | - | - |
|
||||||
|
* | 65536 | - | - | - | - | 8192 | 6120 | - |
|
||||||
|
* | 128k | - | - | - | - | 16384 | - | - |
|
||||||
|
* This ratio is remarkably consistent. No AVR part was ever made with less than 8:1 flash:ram,
|
||||||
|
* nor more than 16:1, since the earliest recognizable AVRs. I am only aware of one exception. Was it some bizarro part
|
||||||
|
* from the dark ages? Nope - it's the surprisingly popular ATmega2560!
|
||||||
|
* The ATmega2560/2561 has only 8k RAM, a 32:1 flash to ram ratio. (to be fair, you are allowed to use external RAM
|
||||||
|
* on those, which was a very rare feature indeed, and that is by far the most widespread part with such a feature - though if you're using the
|
||||||
|
* XMEM interface, you've burned 19 GPIO lines right there.... The ATmega2560 is kind of the "I have a job too big for an AVR.
|
||||||
|
* But I don't know how to program anything else!" part. That is not a compliment.
|
||||||
|
*
|
||||||
|
* | RAM | TX | RX | Amount of RAM implied | Total ram used |
|
||||||
|
* |-------|----|----|-----------------------|----------------|
|
||||||
|
* | < 512 | 16 | 16 | 256b (0/1 w/2k or 4k) | 32b, all 1 port|
|
||||||
|
* | <1024 | 16 | 32 | 512b | 48b or 96b |
|
||||||
|
* | <2048 | 32 | 64 | 1024b | 96b or 192b |
|
||||||
|
* | More | 64 | 64 | 2048b or 3072b | 128b or 256b |
|
||||||
|
*
|
||||||
|
* (the two numbers in final column are given because 0/1-serieas has 1 port, but tiny2 has 2, though if you only use one, you only
|
||||||
|
* get one set of buffers)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Buffer Sizing */
|
||||||
|
|
||||||
|
// The buffer sizes can be overridden in by defining SERIAL_TX_BUFFER either in variant file (as defines in pins_arduino.h) or boards.txt (By passing them as extra flags).
|
||||||
|
// note that buffer sizes must be powers of 2 only.
|
||||||
|
|
||||||
|
#if !defined(SERIAL_TX_BUFFER_SIZE)
|
||||||
|
#if (INTERNAL_SRAM_SIZE < 1024) // 128/256b/512b RAM
|
||||||
|
#define SERIAL_TX_BUFFER_SIZE 16
|
||||||
|
#elif (INTERNAL_SRAM_SIZE < 2048) // 1k RAM
|
||||||
|
#define SERIAL_TX_BUFFER_SIZE 32
|
||||||
|
#else
|
||||||
|
#define SERIAL_TX_BUFFER_SIZE 64 // 2k/3k RAM
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if !defined(SERIAL_RX_BUFFER_SIZE) // could be overridden by boards.txt
|
||||||
|
#if (INTERNAL_SRAM_SIZE < 512) // 128/256b RAM
|
||||||
|
#define SERIAL_RX_BUFFER_SIZE 16
|
||||||
|
// current tx buffer position = SerialClass + txtail + 37
|
||||||
|
#elif (INTERNAL_SRAM_SIZE < 1024) // 512b RAM
|
||||||
|
#define SERIAL_RX_BUFFER_SIZE 32
|
||||||
|
// current tx buffer position = SerialClass + txtail + 53
|
||||||
|
#else
|
||||||
|
#define SERIAL_RX_BUFFER_SIZE 64 // 1k+ RAM
|
||||||
|
// current tx buffer position = SerialClass + txtail + 85
|
||||||
|
// rx buffer position always = SerialClass + rxhead + 21
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
/* Use INTERNAL_SRAM_SIZE instead of RAMEND - RAMSTART, which is vulnerable to
|
||||||
|
* a fencepost error. */
|
||||||
|
#if (SERIAL_TX_BUFFER_SIZE > 256)
|
||||||
|
typedef uint16_t tx_buffer_index_t;
|
||||||
|
#else
|
||||||
|
typedef uint8_t tx_buffer_index_t;
|
||||||
|
#endif
|
||||||
|
// I am not convinced > 256b is safe for the RX buffer....
|
||||||
|
#if (SERIAL_RX_BUFFER_SIZE > 256)
|
||||||
|
typedef uint16_t rx_buffer_index_t;
|
||||||
|
#else
|
||||||
|
typedef uint8_t rx_buffer_index_t;
|
||||||
|
#endif
|
||||||
|
// As noted above, forcing the sizes to be a power of two saves a small
|
||||||
|
// amount of flash, and there's no compelling reason to NOT have them be
|
||||||
|
// a power of two. If this is a problem, since you're already modifying
|
||||||
|
// core, change the lines in UART.cpp where it does & (SERIAL_xX_BUFFERLSIZE-1)
|
||||||
|
// and replace them with % SERIAL_xX_BUFFER_SIZE; where xX is TX or RX.
|
||||||
|
// There are two of each, and the old ending of the line is even commented
|
||||||
|
// out at the end of the line.
|
||||||
|
#if (SERIAL_TX_BUFFER_SIZE & (SERIAL_TX_BUFFER_SIZE - 1))
|
||||||
|
#error "ERROR: TX buffer size must be a power of two."
|
||||||
|
#endif
|
||||||
|
#if (SERIAL_RX_BUFFER_SIZE & (SERIAL_RX_BUFFER_SIZE - 1))
|
||||||
|
#error "ERROR: RX buffer size must be a power of two."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Buffer sizing done */
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
#if !defined(USE_ASM_TXC)
|
||||||
|
#define USE_ASM_TXC 2 // A bit slower than 1 in exchange for halfduplex.
|
||||||
|
//#define USE_ASM_TXC 1 // This *appears* to work? It's the easy one. saves 6b for 1 USART and 44b for each additional one
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(USE_ASM_RXC)
|
||||||
|
#define USE_ASM_RXC 1 // This now works. Saves only 4b for 1 usart but 98 for each additional one
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(USE_ASM_DRE)
|
||||||
|
#define USE_ASM_DRE 1 // This is the hard one...Depends on BOTH buffers, and has that other method of calling it. saves 34b for 1 USART and 68b for each additional one
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#warning "LTO has been disabled! ASM TXC/RXC/DRE not available. USART falling back to the old, flash-inefficient implementation with fewer features."
|
||||||
|
#if defined(USE_ASM_TXC)
|
||||||
|
#undef USE_ASM_TXC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_ASM_RXC)
|
||||||
|
#undef USE_ASM_RXC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_ASM_DRE)
|
||||||
|
#undef USE_ASM_DRE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// savings:
|
||||||
|
// 44 total for 0/1,
|
||||||
|
// 301 for 2-series, which may be nearly 9% of the total flash!
|
||||||
|
// The USE_ASM_* options can be disabled by defining them as 0 (in the same way that buffer sizes can be overridden)
|
||||||
|
// The buffer sizes can be overridden in by defining SERIAL_TX_BUFFER either in variant file (as defines in pins_arduino.h) or boards.txt (By passing them as extra flags).
|
||||||
|
// note that buffer sizes must be powers of 2 only.
|
||||||
|
|
||||||
|
#if USE_ASM_RXC == 1 && !(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16)
|
||||||
|
#error "Assembly RX Complete (RXC) ISR is only supported when RX buffer size are 256, 128, 64, 32 or 16 bytes"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_ASM_DRE == 1 && !((SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) && \
|
||||||
|
(SERIAL_TX_BUFFER_SIZE == 256 || SERIAL_TX_BUFFER_SIZE == 128 || SERIAL_TX_BUFFER_SIZE == 64 || SERIAL_TX_BUFFER_SIZE == 32 || SERIAL_TX_BUFFER_SIZE == 16))
|
||||||
|
#error "Assembly Data Register Empty (DRE) ISR is only supported when both TX and RX buffer sizes are 256, 128, 64, 32 or 16 bytes"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Macros to help the rare few who want sync or MSPI mode */
|
||||||
|
#define syncBegin(port, baud, config, syncopts) ({\
|
||||||
|
if ((config & 0xC0) == 0x40) \
|
||||||
|
{pinConfigure(port.getPin(2), syncopts); \
|
||||||
|
port.begin(baud >> 3, config); \
|
||||||
|
}})
|
||||||
|
|
||||||
|
#define mspiBegin(port, baud, config, invert) ({ \
|
||||||
|
if ((config & 0xC0) == 0xC0) { \
|
||||||
|
pinConfigure(port.getPin(2), invert); \
|
||||||
|
port.begin(baud >> 3, config); \
|
||||||
|
}})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// tinyAVR 0/1-series has 2 bits devoted to RS485, supporting normal (00), RS485 with XDIR driven to control
|
||||||
|
// an external line driver (01), and some other mysterious mode (10) the function of which is unclear. There is
|
||||||
|
// evidence that this poorly documented feature is also present in other hardware, and was only removed on paper.
|
||||||
|
#if !defined(USART_RS485_bm)
|
||||||
|
#if defined(USART_RS485_0_bm)
|
||||||
|
#define USART_RS485_bm USART_RS485_0_bm
|
||||||
|
#else
|
||||||
|
#define USART_RS485_bm 0x01
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(__AVR_ATtinyxy2__) // 8-pin parts use a different set of pin mappings.
|
||||||
|
const uint8_t _usart_pins[][4] = {{PIN_PA6, PIN_PA7, PIN_PA3, PIN_PA0},{PIN_PA1, PIN_PA2, NOT_A_PIN, NOT_A_PIN}};
|
||||||
|
#elif !defined(__AVR_ATtinyx26__) && !defined(__AVR_ATtinyx27__) // Everything that's not a 2-series with >= 20 pins has the standard mappings.
|
||||||
|
const uint8_t _usart_pins[][4] = {{PIN_PB2, PIN_PB3, PIN_PB1, PIN_PB0},{PIN_PA1, PIN_PA2, PIN_PA3, PIN_PA4}};
|
||||||
|
#elif defined(__AVR_ATtinyx26__) || defined(__AVR_ATtinyx27__) // 2-series with 20 or 24 pins have the alt pins for USART1.
|
||||||
|
const uint8_t _usart_pins[][4] = {
|
||||||
|
{PIN_PB2, PIN_PB3, PIN_PB1, PIN_PB0},
|
||||||
|
{PIN_PA1, PIN_PA2, PIN_PA3, PIN_PA4},
|
||||||
|
{PIN_PC2, PIN_PC1, PIN_PC1, PIN_PC3}
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
#error "This can't happen - it doesn't have 8, 14, 20 or 24 pins, or it has 14 pins but no serial port - defines aren't being picked up correctly."
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
#if defined(__AVR_ATtinyxy2__)
|
||||||
|
const uint8_t _usart_pins[][7] = {{PIN_PA6, PIN_PA7, PIN_PA0, PIN_PA3},{PIN_PA1, PIN_PA2, NOT_A_PIN, NOT_A_PIN}};
|
||||||
|
#elif !defined(__AVR_ATtinyx26__) && !defined(__AVR_ATtinyx27__) && defined(MEGATINYCORE_SERIES)
|
||||||
|
const uint8_t _usart_pins[][7] = {{4, 8, 2, 1, 0x22, 0x23, 0x20},{2, 4, 8, 16, 0x01, 0x02, 0x04}};
|
||||||
|
#elif defined(__AVR_ATtinyx26__) || defined(__AVR_ATtinyx27__)
|
||||||
|
const uint8_t _usart_pins[][7] = {
|
||||||
|
{4, 8, 2, 1, 0x22, 0x23, 0x20},
|
||||||
|
{2, 4, 8, 16, 0x01, 0x02, 0x04},
|
||||||
|
{1, 2, 4, 8, 0x40, 0x41, 0x43}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
#define SERIAL_PIN_SETS 2
|
||||||
|
// WHAT THE HELL IS WRONG WITH THIS?!
|
||||||
|
// When this definition is used, row 0 is {PIN_PB3,PIN_PB2,PIN_PB1,PIN_PB0 in this file and row 2 is 0, 0, 0, 0! WTF}
|
||||||
|
// TX, RX, XCK, XDIR
|
||||||
|
/*
|
||||||
|
const uint8_t _usart_pins[][4] = {
|
||||||
|
#if defined(HWSERIAL0_MUX)
|
||||||
|
#if (defined(PIN_HWSERIAL0_TX) && defined(PIN_HWSERIAL0_RX) && defined(PIN_HWSERIAL0_XCK) && defined(PIN_HWSERIAL0_XIR) && PIN_HWSERIAL0_TX != NOT_A_PIN && PIN_HWSERIAL0_RX != NOT_A_PIN)
|
||||||
|
{PIN_HWSERIAL0_TX, PIN_HWSERIAL0_RX, PIN_HWSERIAL0_XCK, PIN_HWSERIAL0_XDIR},
|
||||||
|
#else
|
||||||
|
{NOT_A_PIN, NOT_A_PIN, NOT_A_PIN, NOT_A_PIN},
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(HWSERIAL0_MUX_PINSWAP_1)
|
||||||
|
#if (defined(PIN_HWSERIAL0_TX_PINSWAP_1) && defined(PIN_HWSERIAL0_RX_PINSWAP_1) && defined(PIN_HWSERIAL0_XCK_PINSWAP_1) && defined(PIN_HWSERIAL0_XIR_PINSWAP_1) && PIN_HWSERIAL0_TX_PINSWAP_1 != NOT_A_PIN && PIN_HWSERIAL0_RX_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
{PIN_HWSERIAL0_TX_PINSWAP_1, PIN_HWSERIAL0_RX_PINSWAP_1, PIN_HWSERIAL0_XCK_PINSWAP_1, PIN_HWSERIAL0_XDIR_PINSWAP_1},
|
||||||
|
#else
|
||||||
|
{NOT_A_PIN, NOT_A_PIN, NOT_A_PIN, NOT_A_PIN},
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(USART1) // On 0/1-series, with only one USART, we don't even need the third row.
|
||||||
|
#if defined(HWSERIAL1_MUX_PINSWAP_1)
|
||||||
|
#if (defined(PIN_HWSERIAL1_TX_PINSWAP_1) && defined(PIN_HWSERIAL1_RX_PINSWAP_1) && defined(PIN_HWSERIAL1_XCK_PINSWAP_1) && defined(PIN_HWSERIAL1_XIR_PINSWAP_1) && PIN_HWSERIAL1_TX_PINSWAP_1 != NOT_A_PIN && PIN_HWSERIAL1_RX_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
{PIN_HWSERIAL1_TX_PINSWAP_1, PIN_HWSERIAL1_RX_PINSWAP_1, PIN_HWSERIAL1_XCK_PINSWAP_1, PIN_HWSERIAL1_XDIR_PINSWAP_1},
|
||||||
|
#else
|
||||||
|
{NOT_A_PIN, NOT_A_PIN, NOT_A_PIN, NOT_A_PIN},
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* DANGER DANGER DANGER */
|
||||||
|
/* CHANGING THE MEMBER VARIABLES BETWEEN HERE AND THE OTHER SCARY COMMENT WILL COMPLETELY BREAK SERIAL
|
||||||
|
* WHEN USE_ASM_DRE and/or USE_ASM_RXC is used! */
|
||||||
|
/* DANGER DANGER DANGER */
|
||||||
|
class HardwareSerial : public Stream {
|
||||||
|
protected:
|
||||||
|
volatile USART_t *const _hwserial_module;
|
||||||
|
const uint8_t _module_number;
|
||||||
|
uint8_t _pin_set;
|
||||||
|
|
||||||
|
volatile uint8_t _state; /* 0bvV__fphw */
|
||||||
|
// Of course this has to be volatile! We started writing it in 2.6.x from ISRs in opaque asm.
|
||||||
|
// Bit E ("There's an echo in here") is used if halfduplex mode is enabled, and when RXC reads the buffer,
|
||||||
|
// it also reads state, and if this bit is set, clears the bit and exits the ISR. The opposite of a parity
|
||||||
|
// error, which sets it's bit and exits the isr except that it doesn't care if it's already set) .
|
||||||
|
// v = Overflow has happened at hardware level. Interrupts were disabled for too long.
|
||||||
|
// V = ring buffer overflow has occurred.
|
||||||
|
// _ = reserved
|
||||||
|
// f = One or more framing errors have occurred.
|
||||||
|
// p = One or more parity errors has occurred.
|
||||||
|
// h = half duplex with open drain - disable RX while TX.
|
||||||
|
// w = written (like old _written)
|
||||||
|
|
||||||
|
volatile rx_buffer_index_t _rx_buffer_head;
|
||||||
|
volatile rx_buffer_index_t _rx_buffer_tail;
|
||||||
|
volatile tx_buffer_index_t _tx_buffer_head;
|
||||||
|
volatile tx_buffer_index_t _tx_buffer_tail;
|
||||||
|
|
||||||
|
// Don't put any members after these buffers, since only the first
|
||||||
|
// 32 bytes of this struct can be accessed quickly using the ldd
|
||||||
|
// instruction.
|
||||||
|
volatile uint8_t _rx_buffer[SERIAL_RX_BUFFER_SIZE];
|
||||||
|
volatile uint8_t _tx_buffer[SERIAL_TX_BUFFER_SIZE];
|
||||||
|
/* DANGER DANGER DANGER */
|
||||||
|
/* ANY CHANGES BETWEEN OTHER SCARY COMMENT AND THIS ONE WILL BREAK SERIAL IF THEY CHANGE RAM USED BY CLASS! */
|
||||||
|
/* DANGER DANGER DANGER */
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline HardwareSerial(volatile USART_t *hwserial_module, uint8_t module_number, uint8_t default_pinset);
|
||||||
|
bool pins(uint8_t tx, uint8_t rx);
|
||||||
|
bool swap(uint8_t mux_level = 1);
|
||||||
|
void begin(uint32_t baud) {begin(baud, SERIAL_8N1);}
|
||||||
|
void begin(uint32_t baud, uint16_t options);
|
||||||
|
void end();
|
||||||
|
// printHex!
|
||||||
|
void printHex(const uint8_t b);
|
||||||
|
void printHex(const int8_t b) {printHex((uint8_t ) b); }
|
||||||
|
void printHex(const char b) {printHex((uint8_t ) b); }
|
||||||
|
void printHex(const uint16_t w, bool s = 0);
|
||||||
|
void printHex(const int16_t w, bool s = 0) {printHex((uint16_t)w, s); }
|
||||||
|
void printHex(const uint32_t l, bool s = 0);
|
||||||
|
void printHex(const int32_t l, bool s = 0) {printHex((uint32_t)l, s); }
|
||||||
|
void printHexln(const int8_t b) {printHex((uint8_t ) b); println();}
|
||||||
|
void printHexln(const char b) {printHex((uint8_t ) b); println();}
|
||||||
|
void printHexln(const uint8_t b) {printHex( b); println();}
|
||||||
|
void printHexln(const uint16_t w, bool s = 0) {printHex( w, s); println();}
|
||||||
|
void printHexln(const uint32_t l, bool s = 0) {printHex( l, s); println();}
|
||||||
|
void printHexln(const int16_t w, bool s = 0) {printHex((uint16_t)w, s); println();}
|
||||||
|
void printHexln(const int32_t l, bool s = 0) {printHex((uint32_t)l, s); println();}
|
||||||
|
// The pointer-versions for mass printing uint8_t and uint16_t arrays.
|
||||||
|
uint8_t * printHex( uint8_t* p, uint8_t len, char sep = 0 );
|
||||||
|
uint16_t * printHex( uint16_t* p, uint8_t len, char sep = 0, bool s = 0);
|
||||||
|
volatile uint8_t * printHex(volatile uint8_t* p, uint8_t len, char sep = 0 );
|
||||||
|
volatile uint16_t * printHex(volatile uint16_t* p, uint8_t len, char sep = 0, bool s = 0);
|
||||||
|
uint8_t * printHexln( uint8_t* p, uint8_t len, char sep = 0 ) {
|
||||||
|
uint8_t* ret;
|
||||||
|
ret=printHex(p, len, sep);
|
||||||
|
println(); return ret;
|
||||||
|
}
|
||||||
|
uint16_t * printHexln( uint16_t* p, uint8_t len, char sep = 0, bool s = 0) {
|
||||||
|
uint16_t* ret; ret=printHex(p, len, sep, s); println(); return ret;
|
||||||
|
}
|
||||||
|
volatile uint8_t * printHexln(volatile uint8_t* p, uint8_t len, char sep = 0 ) {
|
||||||
|
volatile uint8_t* ret;
|
||||||
|
ret=printHex(p, len, sep);
|
||||||
|
println();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
volatile uint16_t * printHexln(volatile uint16_t* p, uint8_t len, char sep = 0, bool s = 0) {
|
||||||
|
volatile uint16_t* ret;
|
||||||
|
ret=printHex(p, len, sep, s);
|
||||||
|
println();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int availableForWrite(void);
|
||||||
|
virtual int available(void);
|
||||||
|
virtual int peek(void);
|
||||||
|
virtual int read(void);
|
||||||
|
virtual void flush(void);
|
||||||
|
virtual size_t write(uint8_t ch);
|
||||||
|
inline size_t write(unsigned long n) {return write((uint8_t)n);}
|
||||||
|
inline size_t write(long n) {return write((uint8_t)n);}
|
||||||
|
inline size_t write(unsigned int n) {return write((uint8_t)n);}
|
||||||
|
inline size_t write(int n) {return write((uint8_t)n);}
|
||||||
|
using Print::write; // pull in write(str) and write(buf, size) from Print
|
||||||
|
explicit operator bool() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint8_t autoBaudWFB();
|
||||||
|
void simpleSync();
|
||||||
|
|
||||||
|
uint8_t autobaudWFB_and_wait(uint8_t n);
|
||||||
|
uint8_t waitForSync();
|
||||||
|
uint8_t autobaudWFB_and_request(uint8_t n = 2);
|
||||||
|
uint8_t getStatus() {
|
||||||
|
uint8_t ret = _statuscheck(_hwserial_module->CTRLB, _hwserial_module->STATUS, _state);
|
||||||
|
if ((ret & 0x30) == 0x30) {
|
||||||
|
_hwserial_module->STATUS = USART_ISFIF_bm;
|
||||||
|
#if defined(ERRATA_ISFIF)
|
||||||
|
uint8_t ctrlb _hwserial_module->CTRLB;
|
||||||
|
uint8_t rxoff = ctrlb & (~USART_RXEN_bm);
|
||||||
|
_hwserial_module->CTRLB = rxoff;
|
||||||
|
_hwserial_module->CTRLB = ctrlb;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
_state &= 0x03; // Clear the errors we just reported.
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getPin(uint8_t pin); //wrapper around static _getPin
|
||||||
|
|
||||||
|
// Interrupt handlers - Not intended to be called externally
|
||||||
|
#if !(USE_ASM_RXC == 1 && \
|
||||||
|
(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16))
|
||||||
|
static void _rx_complete_irq(HardwareSerial& uartClass);
|
||||||
|
#endif
|
||||||
|
#if !(USE_ASM_DRE == 1 && \
|
||||||
|
(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) && \
|
||||||
|
(SERIAL_TX_BUFFER_SIZE == 256 || SERIAL_TX_BUFFER_SIZE == 128 || SERIAL_TX_BUFFER_SIZE == 64 || SERIAL_TX_BUFFER_SIZE == 32 || SERIAL_TX_BUFFER_SIZE == 16))
|
||||||
|
static void _tx_data_empty_irq(HardwareSerial& uartClass);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _poll_tx_data_empty(void);
|
||||||
|
static void _set_pins(uint8_t port_num, uint8_t mux_setting, uint8_t enmask);
|
||||||
|
static uint8_t _pins_to_swap(uint8_t port_num, uint8_t tx_pin, uint8_t rx_pin);
|
||||||
|
|
||||||
|
|
||||||
|
/* _statuscheck() - the static side to getStatus(). Static methods have no concept of which instance they are called from. This gives the optimizer more handholds
|
||||||
|
* As you probably know, the optimizer's hands are pretty tightly bound when working with a normal class method, but it has a much freer hand in static methods.
|
||||||
|
* Return value is:
|
||||||
|
* 0HRaaFPHW
|
||||||
|
* H = Hardware RX buffer overflowed because interrupts were disabled for too long while trying to receive data.
|
||||||
|
* R = Ring buffer suffered an overflow. When you check Serial.available, use a while loop to read in characters until nothing is left in the ring buffer.
|
||||||
|
* aa = Autobaud state:
|
||||||
|
* 00 = Disabled
|
||||||
|
* 01 = SERIAL_AUTOBAUD_ENABLED
|
||||||
|
* 10 = SERIAL_AUTOBAUD_SYNC
|
||||||
|
* 11 = SERIAL_AUTOBAUD_BADSYNC - This is Bad News, bro. Baaaaaad news. You tried to do autobaud, but got something that wasn't a sync, and some parts have an errata
|
||||||
|
* that results in the receiver disabling itself until you clear this, turn off RXEN, and turn it back on. This ugly kludge is implemented by getStatus, which calls
|
||||||
|
* this function. Currently only DD parts have this erratum, but I won't trust that until I've seen more recent errata that don't list it (my prediction is anything
|
||||||
|
* with the SFDEN bug has the ISFIF one too, which would be most or all modern AVRs)
|
||||||
|
* F = SERIAL_FRAME_ERROR - A framing error has occurred since you last called this, indicating mismatched baud settings. See Serial Reference.
|
||||||
|
* P = SERIAL_PARITY_ERROR - A parity error has occurred since you last called this, and the bad character did not make it to the application.
|
||||||
|
* When using parity mode, but mismatched baud settings, you will get a mixture of gibberish + framing error set, and
|
||||||
|
* parity errors.
|
||||||
|
* H = SERIAL_HALF_DUPLEX_ENABLED - indicates that half duplex mode is enabled.
|
||||||
|
*
|
||||||
|
* Test for these errors like st = Serial.getStatus(); if (st & SERIAL_FRAME_ERROR) { ... }
|
||||||
|
* For the autobaud ones with 2 bits: if (st & SERIAL_AUTOBAUD_BADSYNC) { // If it wasn't fixed by the core, you'd have to take action. But it is}
|
||||||
|
*/
|
||||||
|
static uint8_t _statuscheck(uint8_t ctrlb, uint8_t status, uint8_t state) {
|
||||||
|
uint8_t ret = state;
|
||||||
|
// We have now: |HW OVF| RING OVF|_____|_______|FrameError|ParityError|HalfDuplex|Written|
|
||||||
|
// now we fill high nybble
|
||||||
|
if ((ctrlb & 0x06) == 0x04) {
|
||||||
|
// Autobaud
|
||||||
|
if (status & USART_BDF_bm) {
|
||||||
|
ret |= SERIAL_AUTOBAUD_SYNC;
|
||||||
|
} else if (status & USART_ISFIF_bm) {
|
||||||
|
ret |= SERIAL_AUTOBAUD_BADSYNC;
|
||||||
|
} else {
|
||||||
|
ret |= SERIAL_AUTOBAUD_ENABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(USART0)
|
||||||
|
extern HardwareSerial Serial0;
|
||||||
|
#endif
|
||||||
|
#if defined(USART1)
|
||||||
|
extern HardwareSerial Serial1;
|
||||||
|
#endif
|
|
@ -0,0 +1,333 @@
|
||||||
|
/* Tone.cpp
|
||||||
|
|
||||||
|
A Tone Generator Library
|
||||||
|
|
||||||
|
Written by Brett Hagman
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
-------- ----------- -------- --------
|
||||||
|
0001 B Hagman 09/08/02 Initial coding
|
||||||
|
0002 B Hagman 09/08/18 Multiple pins
|
||||||
|
0003 B Hagman 09/08/18 Moved initialization from constructor to begin()
|
||||||
|
0004 B Hagman 09/09/26 Fixed problems with ATmega8
|
||||||
|
0005 B Hagman 09/11/23 Scanned prescalars for best fit on 8 bit timers
|
||||||
|
09/11/25 Changed pin toggle method to XOR
|
||||||
|
09/11/25 Fixed timer0 from being excluded
|
||||||
|
0006 D Mellis 09/12/29 Replaced objects with functions
|
||||||
|
0007 M Sproul 10/08/29 Changed #ifdefs from cpu to register
|
||||||
|
0008 S Kanemoto 12/06/22 Fixed for Leonardo by @maris_HY
|
||||||
|
0009 J Reucker 15/04/10 Issue #292 Fixed problems with ATmega8 (thanks to Pete62)
|
||||||
|
0010 jipp 15/04/13 added additional define check #2923
|
||||||
|
0011 E Roy 13/02/18 ported to ATmega4809
|
||||||
|
1.3.3 S Konde 3/30/21 Fixed bug where output could be left high.
|
||||||
|
Fixed bug that would cause anomalies on PORTB through use of
|
||||||
|
a NULL pointer, which points rght at the low IO space. Just return
|
||||||
|
if the pin isn't a valid pin.
|
||||||
|
Fix anomalous silent pointless interrupts for duration of a
|
||||||
|
frequency = 0 "tone" Now I just drive pin low and turn off timer.
|
||||||
|
You asked for 0 Hz? That's 0 Hz alright!
|
||||||
|
In all cases, we ave to avoid leaving it high because
|
||||||
|
that can damage speakers, so I am told..
|
||||||
|
Just return and turn off tone if it's the current tone pin
|
||||||
|
bit_mask was declared volatile; but it shouldn't.
|
||||||
|
_pin was not decared volatile when it should be.
|
||||||
|
Fix bug where high-pitch tones old overflow when calculating
|
||||||
|
duration (the intermediate 2 * frequency was an unsigned int,
|
||||||
|
even though next operation multipolied it by an unsigned long
|
||||||
|
- simply reoved the 2* altogether and divide by 500 at end instead
|
||||||
|
of 1000.
|
||||||
|
Long tones (where frequency * duration > 2.145 billion resulted in an
|
||||||
|
intermediate overflowing before the divide by 1000. removing the
|
||||||
|
2* doubled this limit. But high frequencies and long durations (where
|
||||||
|
frequency * duration would overflow a unsigned long) still break it.
|
||||||
|
I added a way to distribute the division before the multiplication
|
||||||
|
if the duration was long enough to start worrying about. But I decided
|
||||||
|
not to enable by on all parts - it is SUPPORT_LONG_TONES - if defined
|
||||||
|
and 1, when duration is over 2^16, we split that line into
|
||||||
|
(frequency/5) * (duration/100) This version was developed for DxCore
|
||||||
|
1.3.3 and megaTinyCore 2.3.0. The former always uses the bulkier
|
||||||
|
implementation, while the latter does only on parts with 16k+ flash.
|
||||||
|
1.3.6 S Konde 4/17/21 Fixed bug that would break compilation of all sketches if TCB0
|
||||||
|
was used for millis on parts without a TCB1. Now, we intentionally break
|
||||||
|
the compilation only if the user code actually calls tone() or noTone()
|
||||||
|
This version was developed for DxCore 1.3.6 and megaTinyCore 2.3.2
|
||||||
|
1.3.7 S Konde 7/21/21 reorganized code, improved comments. caught a couple of corner
|
||||||
|
cases which could cause odd behavior in obscure cases. Added a check
|
||||||
|
for ENABLE_TCB_PWM which will turn the restoration of PWM mode on and
|
||||||
|
off - will make it easier to move this to DxCore without breakage.
|
||||||
|
I think the cleanup more or less exactly got enough flash back that
|
||||||
|
the coverage of corner cases cost.
|
||||||
|
2.4.0 S Konde Spotted amd fixed a few improperly handled corner cases, and a lot of
|
||||||
|
reorganization of questionable value.
|
||||||
|
2.4.3 S Konde 11/14/21 - Removed debug code which had not been disabled and would
|
||||||
|
print garbage to the serial port.
|
||||||
|
|
||||||
|
**********************************************************************************************************/
|
||||||
|
/************************************************************************************************************
|
||||||
|
S. Konde 7/21/21:
|
||||||
|
I have to wonder a few things here:
|
||||||
|
1. We have option for available tone pins, though very little of whatever else
|
||||||
|
would have been needed to support it is present. I guess on DxCore, that *could*
|
||||||
|
be increased, but a great deal of plumbing for it would also be needed - multiple pins, multiple timers...
|
||||||
|
Generally "a damned big deal" to implement, with a large number of design decisions.
|
||||||
|
What is strange is that I can find no sign of Arduino having supported more than one simultaneous tone pin, which
|
||||||
|
is the only case where this #define makes sense. Unless there were in the past arduinos withonly certain pinsthat could play tone or something
|
||||||
|
have.
|
||||||
|
2. In light of the fact that tone does not let you output multiple tones on multiple pins at once, one could
|
||||||
|
argue that noTone() should shut off the tone and ignore the argument. But the official API says ignore
|
||||||
|
noTone(wrong_pin)
|
||||||
|
My tentative ruling is that:
|
||||||
|
A. Any polyphonic tone functionality belongs in a library, tone should never have AVAILABLE_TONE_PINS =/= 1
|
||||||
|
One approach would be to conditionally compile the ISRs for timers based on a #define
|
||||||
|
that would have to be used before the library is #included; that file could check for ARDUINO_MAIN, and define
|
||||||
|
the ISRs if and only if it is (otherwise it would cause multiple definition error if included by sketch and other library).
|
||||||
|
Another approach would have the functions to control the tone output driven by each timer each in a separate file, or have
|
||||||
|
multiple instances of a TCB tone class, each instantiated in it's own file. The stock core has done this with the
|
||||||
|
HardwareSerial class since the dawn of time. Thus also not creating ISRs for timers we're not 'tone()ing' with.
|
||||||
|
The separate files would also provide an efficient means of keeping the variables that track the state of each
|
||||||
|
timer's tone separate..
|
||||||
|
I suspect that one could do worse than using this file, with liberal addition of the static keyword, as a basis
|
||||||
|
for those timer-specific tone() libraries. Personally, I don't feel like I have a good idea of what features
|
||||||
|
would be desirable for a tone library of that sort. I'm not an audio guy, and I have never connected a buzzer to
|
||||||
|
an MCU. I test these libraries with the 'scope (hell, I wouldn't be able to tell a 1kHz tone from a hole in the
|
||||||
|
ground without looking at it). In that hypothetical library, one design decision would have to be how to deal with
|
||||||
|
the most important part, that being turning the noisy thing off - basically, whether to implement multiTone() and
|
||||||
|
noMultiTone(pin), or to have things like `toneTimerTCB0.tone()/toneTimerTCB0.noTone()`. I am inclined
|
||||||
|
to think the second approach is a more sound one. If anytone wants to write such a librarty, I'm happy to advise
|
||||||
|
B. For the default noTone() that does not involve a library, pin should he required. Arguanbly there should be two
|
||||||
|
versions of it, with and without a pin argument, where the latter would shut down tone wherever it is.
|
||||||
|
************************************************************************************************************/
|
||||||
|
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
|
||||||
|
#define AVAILABLE_TONE_PINS 1
|
||||||
|
|
||||||
|
#if defined(MILLIS_USE_TIMERB0)
|
||||||
|
#if defined(TCB1)
|
||||||
|
#define USE_TIMERB1
|
||||||
|
#else
|
||||||
|
#define TONE_UNAVAILABLE
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define USE_TIMERB0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TONE_UNAVAILABLE
|
||||||
|
static volatile TCB_t* _timer =
|
||||||
|
#if defined(USE_TIMERB0)
|
||||||
|
&TCB0;
|
||||||
|
#endif
|
||||||
|
#if defined(USE_TIMERB1)
|
||||||
|
&TCB1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
volatile uint8_t _pin = NOT_A_PIN;
|
||||||
|
// timerx_toggle_count:
|
||||||
|
// > 0 - duration specified
|
||||||
|
// = 0 - stopped
|
||||||
|
// < 0 - infinitely (until stop() method called, or new tone() called)
|
||||||
|
|
||||||
|
volatile long timer_toggle_count;
|
||||||
|
volatile uint8_t *timer_outtgl_reg;
|
||||||
|
static uint8_t timer_bit_mask;
|
||||||
|
uint8_t timer_cycle_per_tgl;
|
||||||
|
uint8_t timer_cycle_per_tgl_count;
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
static void disableTimer();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// frequency (in hertz) and duration (in milliseconds).
|
||||||
|
#ifndef TONE_UNAVAILABLE
|
||||||
|
void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||||
|
// Believe it or not, we don't need to turn off interrupts!
|
||||||
|
if (frequency == 0) {
|
||||||
|
noTone(pin);
|
||||||
|
/* turn it off (frequency 0, right? Note that this won't do anything
|
||||||
|
* unless that was the pin outputting tone. If it wasn't nothing
|
||||||
|
* will change. But there's no way of outputting a "0 Hz frequency"
|
||||||
|
* and a 1 Hz frequency makes even less sense. (they did ask for 0)
|
||||||
|
* But outputting a 1 Hz frequency on nothing (bitmask was cleared)
|
||||||
|
* achieves nothing except generate do-nothing interrupts regularly.
|
||||||
|
* When it is clear that provided arguments are sufficiently wrong
|
||||||
|
* that you can't do anything useful with them, at least don't
|
||||||
|
* do useless things instead. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get pin related info
|
||||||
|
uint8_t bit_mask = digitalPinToBitMask(pin);
|
||||||
|
if (bit_mask == NOT_A_PIN) return;
|
||||||
|
/* Same logic as above holds here except what it did was actively
|
||||||
|
* disruptive. When library code takes user supplied pins and looks
|
||||||
|
* up bitmasks and position, always check that one of them isn;t
|
||||||
|
* NOT_A_PIN - before you get the PORT struct. Because the port
|
||||||
|
* struct function cannot have it's result used safely. For bad
|
||||||
|
* pins, yes it returns a null pointer, 0x0000. But not only is
|
||||||
|
* that a valid register, it's an SFR (VPORTA.DIR)! So we don't
|
||||||
|
* want to end up writing to that. The convention of NULL pointers
|
||||||
|
* being invalid to work with is NOT safe to assume on embedded
|
||||||
|
* systems. */
|
||||||
|
|
||||||
|
// Serial.println(frequency);
|
||||||
|
long toggle_count;
|
||||||
|
// Calculate the toggle count
|
||||||
|
if (duration > 0) { // Duration defined
|
||||||
|
#if defined(SUPPORT_LONG_TONES) && SUPPORT_LONG_TONES == 1
|
||||||
|
if (duration > 65536) {
|
||||||
|
toggle_count = (frequency / 5) * (duration / 100);
|
||||||
|
} else {
|
||||||
|
toggle_count = (frequency * duration) / 500;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
toggle_count = (frequency * duration) / 500;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
// Duration not specified -> infinite
|
||||||
|
// Represented internally by toggle_count = -1
|
||||||
|
toggle_count = -1;
|
||||||
|
}
|
||||||
|
// Calculate compare value
|
||||||
|
int8_t divisionfactor = 0; // no prescale, toggles at twice the frequency
|
||||||
|
|
||||||
|
// Timer settings -- will be type B timer or bust....
|
||||||
|
uint32_t compare_val = ((F_CPU / frequency) >> 1);
|
||||||
|
// We're are however disabling the timer. There may be one final interrupt at the moment we do so
|
||||||
|
// but that's not a problem
|
||||||
|
if (compare_val < 0x10000) { /* previously this tested for divisionfactor == 1,
|
||||||
|
* but that relied on us having already gone through the while loop to
|
||||||
|
* adjust it, which we haven't done yet, but we want to do this *after* the
|
||||||
|
* timeconsuming division operation, but *before* we actually change any
|
||||||
|
* other settings, because this is the point at which we stop the timer -
|
||||||
|
* hence it needs to be the first to be set if we want to leave interrupts on*/
|
||||||
|
_timer->CTRLA = TCB_CLKSEL_DIV1_gc;
|
||||||
|
} else {
|
||||||
|
_timer->CTRLA = TCB_CLKSEL_DIV2_gc;
|
||||||
|
divisionfactor--;
|
||||||
|
}
|
||||||
|
while ((compare_val > 0x10000) && (divisionfactor < 6)) {
|
||||||
|
// If the "else" branch above was followed, this is always true initially.
|
||||||
|
compare_val = compare_val >> 1;
|
||||||
|
divisionfactor++;
|
||||||
|
}
|
||||||
|
if (--compare_val > 0xFFFF) {
|
||||||
|
// if still too high, divisionfactor reached 6 (each phase lasts 64 overflows), meaning the
|
||||||
|
// user is using tone() to generate something that is not, by any stretch of the word, a
|
||||||
|
// "tone", and they should be generating it through other means.
|
||||||
|
compare_val = 0xFFFF; // do the best we can
|
||||||
|
}
|
||||||
|
// Serial.println(compare_val);
|
||||||
|
// Serial.println(divisionfactor);
|
||||||
|
// Anyway - so we know that the new pin is valid....
|
||||||
|
if (_pin != pin) { // ...let's see if we're using it already.
|
||||||
|
if (_pin != NOT_A_PIN) { // If not - were we using one before?
|
||||||
|
// If we were, we're gonna be in a world of hurt if we don't
|
||||||
|
// turn it off before we actually start reconfiguring stuff
|
||||||
|
// *(timer_outtgl_reg - 5) = timer_bit_mask; // pinMode(_pin, INPUT); (write dirclr for old pin)
|
||||||
|
// Apparently maybe it is intended for the pin to be left as an output?
|
||||||
|
// It does need to be LOW though, otherwise it can damage some speakers!
|
||||||
|
*(timer_outtgl_reg - 1) = timer_bit_mask; // digitalWrite(_pin, LOW); (write outclr for old pin)
|
||||||
|
}
|
||||||
|
// whether or not we _were_ using a pin, we are now, so configure the new one as an output...
|
||||||
|
PORT_t *port = digitalPinToPortStruct(pin); // Since not known at compile time, use PORTs not VPORTS.
|
||||||
|
timer_bit_mask = bit_mask; // We no longer need old pin's bit_mask
|
||||||
|
timer_outtgl_reg = (volatile uint8_t *) &(port->OUTTGL); // or toggle register.
|
||||||
|
*(timer_outtgl_reg - 1) = bit_mask; // digitalWrite(pin, LOW);
|
||||||
|
*(timer_outtgl_reg - 6) = bit_mask; // pinMode(pin, OUTPUT);
|
||||||
|
}
|
||||||
|
// Save the results of our calculations
|
||||||
|
timer_toggle_count = toggle_count;
|
||||||
|
timer_cycle_per_tgl = 1 << divisionfactor; // 1, 2, 4, 8, 16, 32, or 64 - toggle pin once per this many cycles...
|
||||||
|
timer_cycle_per_tgl_count = timer_cycle_per_tgl; // running count of remaining toggle cycles.
|
||||||
|
_timer->CCMP = compare_val; // ...and each cycle is this many timer ticks long
|
||||||
|
_timer->CTRLB = TCB_CNTMODE_INT_gc; // Set mode to Periodic Interrupt mode.
|
||||||
|
_timer->CNT = 0; // Not strictly necessary, but ensures there's no glitch.
|
||||||
|
_pin = pin; // Record new pin number.
|
||||||
|
_timer->INTCTRL = TCB_CAPTEI_bm; // Enable the interrupt (flag is already cleared)
|
||||||
|
_timer->CTRLA |= TCB_ENABLE_bm; // Everything is ready - Enable timer!
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void tone(__attribute__ ((unused)) uint8_t pin, __attribute__ ((unused)) unsigned int frequency, __attribute__ ((unused)) unsigned long duration) {
|
||||||
|
badCall("TCB0 used for millis, no other TCBs on this part; tone requires exclusive use of a type B timer, use a differemt millis timer or a tinyAVR with a second TCB (any 2-series, or 1-series with 16k+ flash)");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// pin which currently is being used for a tone
|
||||||
|
#ifndef TONE_UNAVAILABLE
|
||||||
|
void noTone(uint8_t pin) {
|
||||||
|
if (pin == _pin) {
|
||||||
|
uint8_t old_SREG = SREG; // Save SREG
|
||||||
|
cli(); // Interrupts off
|
||||||
|
timer_toggle_count = 0; // clear this one
|
||||||
|
_pin = NOT_A_PIN;
|
||||||
|
disableTimer(); // End with pin LOW, otherwise can damage some speakers.
|
||||||
|
SREG = old_SREG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void noTone(__attribute__ ((unused)) uint8_t pin) {
|
||||||
|
badCall("TCB0 used for millis, no other TCBs on this part; tone requires exclusive use of a type B timer, use a differemt millis timer or a tinyAVR with a second TCB (any 2-series, or 1-series with 16k+ flash)");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TONE_UNAVAILABLE
|
||||||
|
// helper function for ending tone.
|
||||||
|
/* Works for all timers -- the timer being disabled will go back to the
|
||||||
|
configuration it had on startup */
|
||||||
|
static void disableTimer() {
|
||||||
|
_timer->CTRLA = 0; // disable timer
|
||||||
|
_timer->INTCTRL = 0; // disable the timer interrupts, otherwise if something else configures it and assumes that it's in the reset configuration
|
||||||
|
// and so doesn't write INTCTRL (because it doesn't use interrupts), the tone ISR could be called inappropriately.
|
||||||
|
_timer->INTFLAGS = 0xFF; // Make sure the flags are cleared (flags can be set without their interrupt being enabled, these will fire as soon as it is)
|
||||||
|
_pin = NOT_A_PIN; // and clear _pin.
|
||||||
|
#if defined(ENABLE_TCB_PWM) && ENABLE_TCB_PWM == 1
|
||||||
|
// RESTORE PWM FUNCTIONALITY, for use with cores that use the TCBs for PWM.
|
||||||
|
// This section adds 12 words of flash and as many clock cycles, and could probably be done less expensively if the compiler was smarter about
|
||||||
|
// using ldd/std when it offered a speed/flash advantage. But it isn't, so it doesn't. =
|
||||||
|
_timer->CTRLB = (TCB_CNTMODE_PWM8_gc); // 8 bit PWM mode, but do not enable output yet, will do in analogWrite()
|
||||||
|
_timer->CCMPL = PWM_TIMER_PERIOD; // Assign 8-bit period, probably 254.
|
||||||
|
_timer->CCMPH = PWM_TIMER_COMPARE; // We need to set low byte in order for high byte to be written (see silicon errata).
|
||||||
|
_timer->CTRLA = (TCB_CLKSEL_CLKTCA_gc) | (TCB_ENABLE_bm); // Use TCA clock (250kHz) and enable
|
||||||
|
#endif
|
||||||
|
*(timer_outtgl_reg - 1) = timer_bit_mask; // Write OUTCLR, so we are sure to end with pin LOW.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if !defined(TONE_UNAVAILABLE)
|
||||||
|
#if defined(USE_TIMERB0)
|
||||||
|
ISR(TCB0_INT_vect)
|
||||||
|
#elif defined(USE_TIMERB1)
|
||||||
|
ISR(TCB1_INT_vect)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (!(--timer_cycle_per_tgl_count)) { // Are we ready to toggle? pre-decrement, then see if we're at 0 yet.
|
||||||
|
timer_cycle_per_tgl_count = timer_cycle_per_tgl; // reset countdown
|
||||||
|
*timer_outtgl_reg = timer_bit_mask; // toggle the pin
|
||||||
|
if (timer_toggle_count > 0) { // if duration was specified, decrement toggle count.
|
||||||
|
timer_toggle_count--;
|
||||||
|
} else if (timer_toggle_count == 0) { // If toggle count = 0 we are done.
|
||||||
|
disableTimer();
|
||||||
|
} // otherwise timer_toggle_count wasn't supplied, go on until noTone() called
|
||||||
|
}
|
||||||
|
_timer->INTFLAGS = TCB_CAPT_bm; // Clear flag
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void disableTimer() {
|
||||||
|
badCall("TCB0 used for millis, no other TCBs on this part; tone requires exclusive use of a type B timer, use a differemt millis timer or a tinyAVR with a second TCB (any 2-series, or 1-series with 16k+ flash)");
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,888 @@
|
||||||
|
/* UART.cpp - Hardware serial library, main file.
|
||||||
|
* This library is free software released under LGPL 2.1.
|
||||||
|
* See License.md for more information.
|
||||||
|
* This file is part of megaTinyCore.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 Nicholas Zambetti, Modified by
|
||||||
|
* 11/23/2006 David A. Mellis, 9/20/2010 Mark Sproul,
|
||||||
|
* 8/24/2012 Alarus, 12/3/2013 Matthijs Kooijman
|
||||||
|
* Others (unknown) 2013-2017, 2017-2021 Spence Konde
|
||||||
|
* and 2021 MX682X
|
||||||
|
*
|
||||||
|
* See UART.h for more of a record of changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <util/atomic.h>
|
||||||
|
//#include <avr/io.h>
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "UART.h"
|
||||||
|
#include "UART_private.h"
|
||||||
|
|
||||||
|
// this next line disables the entire UART.cpp if there's no hardware serial
|
||||||
|
#if defined(USART0) || defined(USART1) || defined(USART2) || defined(USART3) || defined(USART4) || defined(USART5)
|
||||||
|
|
||||||
|
#if defined(HAVE_HWSERIAL0) || defined(HAVE_HWSERIAL1) || defined(HAVE_HWSERIAL2) || defined(HAVE_HWSERIAL3) || defined(HAVE_HWSERIAL4) || defined(HAVE_HWSERIAL5)
|
||||||
|
// macro to guard critical sections when needed for large TX buffer sizes
|
||||||
|
#if (SERIAL_TX_BUFFER_SIZE > 256)
|
||||||
|
#define TX_BUFFER_ATOMIC ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||||||
|
#else
|
||||||
|
#define TX_BUFFER_ATOMIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*## ### ####
|
||||||
|
# # # #
|
||||||
|
# ### ####
|
||||||
|
# # # #
|
||||||
|
### #### # */
|
||||||
|
/* There ain't no such thing as a free lunch. Every new feature makes these worse.
|
||||||
|
USE_ASM_TXC 2 takes an additional 2 words/2 clocks vs 1
|
||||||
|
USE_ASM_RXC 2 takes an additional 5 words/5 clocks vs 1
|
||||||
|
RXC takes another 3 words/4 clocks if SDF errata compensation is needed.
|
||||||
|
Both USE_ASM_RXC and USE_ASM_TXC must be mode 2 for half duplex to work
|
||||||
|
|
||||||
|
Transmit Complete for Half Duplex
|
||||||
|
This is a more efficient and scalable version of the TXC ISR, the original implementation is below:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(USART1)
|
||||||
|
ISR(USART1_TXC_vect) {
|
||||||
|
uint8_t ctrla;
|
||||||
|
while (USART1.STATUS & USART_RXCIF_bm) {
|
||||||
|
ctrla = USART1.RXDATAL;
|
||||||
|
}
|
||||||
|
ctrla = USART1.CTRLA;
|
||||||
|
ctrla |= USART_RXCIE_bm; // turn on receive complete
|
||||||
|
ctrla &= ~USART_TXCIE_bm; // turn off transmit complete
|
||||||
|
USART1.CTRLA = ctrla;
|
||||||
|
}
|
||||||
|
|
||||||
|
In the USARTn.cpp, there's something like this that puts the low byte of the address of the USART into r30
|
||||||
|
after saving the register. Then it just jumps to this routine, which loads the (always the same) high byte!
|
||||||
|
and finishes up clearing out the other register we will need and saving the SREG. The logic is very simple,
|
||||||
|
It's just ugly. It gets worse for the other interrupts because we have to work with the class, not just the
|
||||||
|
hardware. Crucially the only thing different betweren the USARTs here isthe addressthey're working with.
|
||||||
|
Much of the benefit comes from being able to get the benefits of functionsin terms of flash use without the
|
||||||
|
penalties that come with using a true CALL instruction in an ISR (50-80 byte prologue + epiloge), and also
|
||||||
|
being aware that the X register can't do displacement when planning what goes in which regiseser... which
|
||||||
|
is not avr-gcc's strong suite, and often ends up displacing from the X with adiw/sbiw spam. savings for one
|
||||||
|
copy of it is small. Savings for several is gets large fast! Performance is better, but not much.
|
||||||
|
Biggest advantage is for 2-series with the dual UARTs, but potentially as little as 4k of flash.
|
||||||
|
|
||||||
|
TXC isr starts from this:
|
||||||
|
ISR(USART1_TXC_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t" // 1
|
||||||
|
"ldi r30, 0x20" "\n\t" // 1
|
||||||
|
"rjmp do_txc" "\n\t" // 1
|
||||||
|
:::);
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if USE_ASM_TXC == 2
|
||||||
|
void __attribute__((naked)) __attribute__((used)) __attribute__((noreturn)) _do_txc(void) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"_do_txc:" "\n\t" // We start out 11-13 clocks after the interrupt
|
||||||
|
"push r24" "\n\t" // r30 and r31 pushed before this.
|
||||||
|
"in r24, 0x3f" "\n\t" // Save SREG
|
||||||
|
"push r24" "\n\t" //
|
||||||
|
"push r25" "\n\t" //
|
||||||
|
"push r28" "\n\t" //
|
||||||
|
"push r29" "\n\t" //
|
||||||
|
"ldd r28, Z + 8" "\n\t" // Load USART into Y pointer, low byte
|
||||||
|
"ldi r29, 0x08" "\n\t" // all USARTs are 0x08n0 where n is an even hex digit.
|
||||||
|
"ldd r25, Y + 5" "\n\t" // Y + 5 = USARTn.CTRLA read CTRLA
|
||||||
|
"_txc_flush_rx:" "\n\t" // start of rx flush loop.
|
||||||
|
"ld r24, Y" "\n\t" // Y + 0 = USARTn.RXDATAL rx data
|
||||||
|
"ldd r24, Y + 4" "\n\t" // Y + 4 = USARTn.STATUS
|
||||||
|
"sbrc r24, 7" "\n\t" // if RXC bit is clear...
|
||||||
|
"rjmp _txc_flush_rx" "\n\t" // .... skip this jump to remove more from the buffer.
|
||||||
|
"andi r25, 0xBF" "\n\t" // clear TXCIE
|
||||||
|
"ori r25, 0x80" "\n\t" // set RXCIE
|
||||||
|
"std Y + 5, r25" "\n\t" // store CTRLA
|
||||||
|
// "ldd r24, Z + 12" "\n\t"
|
||||||
|
// "ahha, always, true" "\n\t" // wait, if we're in TXC, We are in half duplex mode, duuuuh
|
||||||
|
// "sbrs r24, 2" "\n\t" // if we're in half duplex skip...
|
||||||
|
// "rjmp .+ 6" "\n\t" // a jump over the next three instructoins. Do do them iff in half duplex only
|
||||||
|
// "ori r24, 0x10" "\n\t" // add the "there's an echo in here" bit
|
||||||
|
// "std Z + 12, r24" "\n\t" // Store modified state
|
||||||
|
"pop r29" "\n\t"
|
||||||
|
"pop r28" "\n\t"
|
||||||
|
"pop r25" "\n\t"
|
||||||
|
"pop r24" "\n\t" // pop r24 to get old SREG back
|
||||||
|
"out 0x3F, r24" "\n\t" // restore sreg.
|
||||||
|
"pop r24" "\n\t" // pop r24 restore it
|
||||||
|
"pop r31" "\n\t" // and r31
|
||||||
|
"pop r30" "\n\t" // Pop the register the ISR did
|
||||||
|
"reti" "\n" // return from the interrupt.
|
||||||
|
::
|
||||||
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#elif USE_ASM_TXC == 1
|
||||||
|
void __attribute__((naked)) __attribute__((used)) __attribute__((noreturn)) _do_txc(void) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"_do_txc:" "\n\t" //
|
||||||
|
"push r31" "\n\t" // push other half of Z register.
|
||||||
|
"push r24" "\n\t" // push r24
|
||||||
|
"in r24, 0x3f" "\n\t" // save sreg to r24
|
||||||
|
"push r24" "\n\t" // and push that. r30 pushed and loaded by ISR already.
|
||||||
|
"ldi r31, 0x08" "\n\t" // all USARTs are 0x08n0 where n is an even hex digit.
|
||||||
|
"_txc_flush_rx:" "\n\t" // start of rx flush loop.
|
||||||
|
"ld r24, Z" "\n\t" // Z + 0 = USARTn.RXDATAL rx data
|
||||||
|
"ldd r24, Z + 4" "\n\t" // Z + 4 = USARTn.STATUS
|
||||||
|
"sbrc r24, 7" "\n\t" // if RXC bit is set...
|
||||||
|
"rjmp _txc_flush_rx" "\n\t" // .... skip this jump to remove more from the buffer.
|
||||||
|
"ldd r24, Z + 5" "\n\t" // Z + 5 = USARTn.CTRLA read CTRLA
|
||||||
|
"andi r24, 0xBF" "\n\t" // clear TXCIE
|
||||||
|
"ori r24, 0x80" "\n\t" // set RXCIE
|
||||||
|
"std Z + 5, r24" "\n\t" // store CTRLA
|
||||||
|
"pop r24" "\n\t" // pop r24, xcontaining old sreg.
|
||||||
|
"out 0x3f, r24" "\n\t" // restore it
|
||||||
|
"pop r24" "\n\t" // pop r24 to get it's old value back
|
||||||
|
"pop r31" "\n\t" // and r31
|
||||||
|
"pop r30" "\n\t" // Pop the register the ISR pushed
|
||||||
|
"reti" "\n" // return from the interrupt.
|
||||||
|
::);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
We are starting from this:
|
||||||
|
ISR(USART0_RXC_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t"
|
||||||
|
"push r31" "\n\t"
|
||||||
|
:::);
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"rjmp do_rxc" "\n\t"
|
||||||
|
::"z"(&Serialn));
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ((USE_ASM_RXC == 1) && (SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) )
|
||||||
|
void __attribute__((naked)) __attribute__((used)) __attribute__((noreturn)) _do_rxc(void) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"_do_rxc:" "\n\t" // We start out 11-13 clocks after the interrupt
|
||||||
|
"push r18" "\n\t" // r30 and r31 pushed before this.
|
||||||
|
"in r18, 0x3f" "\n\t" // Save SREG
|
||||||
|
"push r18" "\n\t" //
|
||||||
|
"push r19" "\n\t" // Ugh we needed another register....
|
||||||
|
"push r24" "\n\t" //
|
||||||
|
"push r25" "\n\t" //
|
||||||
|
"push r28" "\n\t" //
|
||||||
|
"push r29" "\n\t" //
|
||||||
|
"ldd r28, Z + 8" "\n\t" // Load USART into Y pointer
|
||||||
|
// "ldd r29, Z + 9" "\n\t" // We interact with the USART only this once
|
||||||
|
"ldi r29, 0x08" "\n\t" // High byte always 0x08 for USART peripheral: Save-a-clock. 11 clocks to here
|
||||||
|
#if defined(ERRATA_USART_WAKE) // This bug appears to be near-universal, 4 clocks to workaround.
|
||||||
|
"ldd r18, Y + 6" "\n\t"
|
||||||
|
"andi r18, 0xEF" "\n\t"
|
||||||
|
"std Y + 6, r18" "\n\t" // turn off SFD interrupt before reading RXDATA so we don't corrupt the next character.
|
||||||
|
#endif
|
||||||
|
"ldd r24, Y + 1" "\n\t" // Y + 1 = USARTn.RXDATAH - load high byte first - 16 clocks from here to next
|
||||||
|
"ld r25, Y" "\n\t" // Y + 0 = USARTn.RXDATAL - then low byte of RXdata
|
||||||
|
"andi r24, 0x46" "\n\t" // extract framing, parity bits.
|
||||||
|
"lsl r24" "\n\t" // leftshift them one place
|
||||||
|
"ldd r19, Z + 12" "\n\t" // load _state
|
||||||
|
"or r19, r24" "\n\t" // bitwise or with errors extracted from _state
|
||||||
|
"sbrc r24, 2" "\n\t" // if there's a parity error, then do nothing more (note the leftshift).
|
||||||
|
"rjmp _end_rxc" "\n\t" // Copies the behavior of stock implementation - framing errors are ok, apparently...
|
||||||
|
//#if USE_ASM_TXC == 2 && USE_ASM_RXC == 2
|
||||||
|
//"sbic 0x1F, 0" "\n\t"
|
||||||
|
//"sbi 0x01, 2" "\n\t"
|
||||||
|
//"sbrs r19, 4" "\n\t" // Is there an echo in here?
|
||||||
|
//"rjmp storechar" "\n\t" // if not skip these next isns "\n\t"
|
||||||
|
//"sbic 0x1F, 0" "\n\t"
|
||||||
|
//"sbi 0x02, 4" "\n\t"
|
||||||
|
//"andi r19, 0xEF" "\n\t" // clear t
|
||||||
|
//"rjmp _end_rxc" "\n\t"
|
||||||
|
// "storechar:"
|
||||||
|
//#endif
|
||||||
|
"ldd r28, Z + 13" "\n\t" // load current head index
|
||||||
|
"ldi r24, 1" "\n\t" // Clear r24 and initialize it with 1
|
||||||
|
"add r24, r28" "\n\t" // add current head index to it
|
||||||
|
#if SERIAL_RX_BUFFER_SIZE == 256
|
||||||
|
// No additional action needed, head wraps naturally.
|
||||||
|
#elif SERIAL_RX_BUFFER_SIZE == 128
|
||||||
|
"andi r24, 0x7F" "\n\t" // Wrap the head around
|
||||||
|
#elif SERIAL_RX_BUFFER_SIZE == 64
|
||||||
|
"andi r24, 0x3F" "\n\t" // Wrap the head around
|
||||||
|
#elif SERIAL_RX_BUFFER_SIZE == 32
|
||||||
|
"andi r24, 0x1F" "\n\t" // Wrap the head around
|
||||||
|
#elif SERIAL_RX_BUFFER_SIZE == 16
|
||||||
|
"andi r24, 0x0F" "\n\t" // Wrap the head around
|
||||||
|
#endif
|
||||||
|
"ldd r18, Z + 14" "\n\t" // load tail index This to _end_rxc is 11 clocks unless the buffer was full, in which case it's 8.
|
||||||
|
"cp r18, r24" "\n\t" // See if head is at tail. If so, buffer full. The incoming data is discarded,
|
||||||
|
"breq _buff_full_rxc" "\n\t" // because there is noplace to put it, and we just restore state and leave.
|
||||||
|
"add r28, r30" "\n\t" // r28 has what would be the next index in it.
|
||||||
|
"mov r29, r31" "\n\t" // and this is the high byte of serial instance
|
||||||
|
"ldi r18, 0" "\n\t" // need a known zero to carry.
|
||||||
|
"adc r29, r18" "\n\t" // carry - Y is now pointing 17 bytes before head
|
||||||
|
"std Y + 17, r25" "\n\t" // store the new char in buffer
|
||||||
|
"std Z + 13, r24" "\n\t" // write that new head index.
|
||||||
|
"_end_rxc:" "\n\t"
|
||||||
|
"std Z + 12, r19" "\n\t" // record new state including new errors
|
||||||
|
// Epilogue: 9 pops + 1 out + 1 reti +1 std = 24 clocks
|
||||||
|
"pop r29" "\n\t" // Y Pointer was used for head and usart.
|
||||||
|
"pop r28" "\n\t" //
|
||||||
|
"pop r25" "\n\t" // r25 held the received character
|
||||||
|
"pop r24" "\n\t" // r24 held rxdatah, then the new head.
|
||||||
|
"pop r19" "\n\t" // restore r19 which held the value that State will have after this.
|
||||||
|
"pop r18" "\n\t" // Restore saved SREG
|
||||||
|
"out 0x3f, r18" "\n\t" // and write back
|
||||||
|
"pop r18" "\n\t" // used as tail offset, and then as known zero.
|
||||||
|
"pop r31" "\n\t" // end with Z which the isr pushed to make room for
|
||||||
|
"pop r30" "\n\t" // pointer to serial instance
|
||||||
|
"reti" "\n\t" // return
|
||||||
|
"_buff_full_rxc:" "\n\t" // potential improvement: move _buff_full_rxc to after the reti, and then rjmp back, saving 2 clocks for the common case
|
||||||
|
"ori r19, 0x40" "\n\t" // record that there was a ring buffer overflow. 1 clk
|
||||||
|
"rjmp _end_rxc" "\n\t" // and now jump back to end. That way we don't need to jump over this in the middle of the common case.
|
||||||
|
::); // total: 77 or 79 clocks, just barely squeaks by for cyclic RX of up to RX_BUFFER_SIZE characters.
|
||||||
|
__builtin_unreachable();
|
||||||
|
|
||||||
|
}
|
||||||
|
#elif defined(USE_ASM_RXC) && USE_ASM_RXC == 1
|
||||||
|
#warning "USE_ASM_RXC is defined and this has more than one serial port, but the buffer size is not supported, falling back to the classical RXC."
|
||||||
|
#else
|
||||||
|
#if defined(PERMIT_USART_WAKE)
|
||||||
|
#error "USART Wake is not supported by the non-ASM RXC interrupt handler"
|
||||||
|
#endif
|
||||||
|
void HardwareSerial::_rx_complete_irq(HardwareSerial& HardwareSerial) {
|
||||||
|
// if (bit_is_clear(*_rxdatah, USART_PERR_bp)) {
|
||||||
|
uint8_t rxDataH = HardwareSerial._hwserial_module->RXDATAH;
|
||||||
|
uint8_t c = HardwareSerial._hwserial_module->RXDATAL; // no need to read the data twice. read it, then decide what to do
|
||||||
|
rx_buffer_index_t rxHead = HardwareSerial._rx_buffer_head;
|
||||||
|
|
||||||
|
if (!(rxDataH & USART_PERR_bm)) {
|
||||||
|
// No Parity error, read byte and store it in the buffer if there is room
|
||||||
|
// unsigned char c = HardwareSerial._hwserial_module->RXDATAL;
|
||||||
|
#if SERIAL_RX_BUFFER_SIZE > 256
|
||||||
|
rx_buffer_index_t i = (uint16_t)(rxHead + 1) % SERIAL_RX_BUFFER_SIZE;
|
||||||
|
#else
|
||||||
|
rx_buffer_index_t i = (uint8_t)(rxHead + 1) % SERIAL_RX_BUFFER_SIZE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// if we should be storing the received character into the location
|
||||||
|
// just before the tail (meaning that the head would advance to the
|
||||||
|
// current location of the tail), we're about to overflow the buffer
|
||||||
|
// and so we don't write the character or advance the head.
|
||||||
|
if (i != HardwareSerial._rx_buffer_tail) {
|
||||||
|
HardwareSerial._rx_buffer[rxHead] = c;
|
||||||
|
HardwareSerial._rx_buffer_head = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
DRE starts just like RXC
|
||||||
|
ISR(USART0_DRE_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
push r30
|
||||||
|
push r31 "\n\t"
|
||||||
|
:::);
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"rjmp do_dre" "\n\t"
|
||||||
|
::"z"(&Serialn));
|
||||||
|
__builtin_unreachable();
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if USE_ASM_DRE == 1 && (SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) && \
|
||||||
|
(SERIAL_TX_BUFFER_SIZE == 256 || SERIAL_TX_BUFFER_SIZE == 128 || SERIAL_TX_BUFFER_SIZE == 64 || SERIAL_TX_BUFFER_SIZE == 32 || SERIAL_TX_BUFFER_SIZE == 16)
|
||||||
|
void __attribute__((naked)) __attribute__((used)) __attribute__((noreturn)) _do_dre(void) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"_do_dre:" "\n\t"
|
||||||
|
"push r18" "\n\t"
|
||||||
|
"in r18, 0x3F" "\n\t"
|
||||||
|
"push r18" "\n\t"
|
||||||
|
"push r24" "\n\t"
|
||||||
|
"push r25" "\n\t"
|
||||||
|
"push r26" "\n\t"
|
||||||
|
"push r27" "\n\t"
|
||||||
|
"set" "\n\t" // SEt the T flag - we use this to determine how we got here and hence whether to rjmp to end of poll or reti
|
||||||
|
"_poll_dre:" "\n\t"
|
||||||
|
"push r28" "\n\t"
|
||||||
|
"push r29" "\n\t"
|
||||||
|
"ldi r18, 0" "\n\t"
|
||||||
|
"ldd r28, Z + 8" "\n\t" // usart in Y
|
||||||
|
// "ldd r29, Z + 9" "\n\t" // usart in Y
|
||||||
|
"ldi r29, 0x08" "\n\t" // High byte always 0x08 for USART peripheral: Save-a-clock.
|
||||||
|
"ldd r25, Z + 16" "\n\t" // tx tail in r25
|
||||||
|
"movw r26, r30" "\n\t" // copy of serial in X
|
||||||
|
"add r26, r25" "\n\t" // SerialN + txtail
|
||||||
|
"adc r27, r18" "\n\t" // X = &Serial + txtail
|
||||||
|
#if SERIAL_RX_BUFFER_SIZE == 256 // RX buffer determines offset from start of class to TX buffer
|
||||||
|
"subi r26, 0xEF" "\n\t" // There's no addi/adci, so we instead subtract (65536-(offset we want to add))
|
||||||
|
"sbci r27, 0xFE" "\n\t" // +273
|
||||||
|
"ld r24, X" "\n\t" // grab the character
|
||||||
|
#elif SERIAL_RX_BUFFER_SIZE == 128
|
||||||
|
"subi r26, 0x6F" "\n\t" //
|
||||||
|
"sbci r27, 0xFF" "\n\t" // +145
|
||||||
|
"ld r24, X" "\n\t" // grab the character
|
||||||
|
#elif SERIAL_RX_BUFFER_SIZE == 64
|
||||||
|
"subi r26, 0xAF" "\n\t" //
|
||||||
|
"sbci r27, 0xFF" "\n\t" // +81
|
||||||
|
"ld r24, X" "\n\t" // grab the character
|
||||||
|
#elif SERIAL_RX_BUFFER_SIZE == 32
|
||||||
|
"adiw r26, 0x31" "\n\t" // +49
|
||||||
|
"ld r24, X" "\n\t" // grab the character
|
||||||
|
#elif SERIAL_RX_BUFFER_SIZE == 16
|
||||||
|
"adiw r26, 0x21" "\n\t" // +33
|
||||||
|
"ld r24, X" "\n\t" // grab the character
|
||||||
|
#endif
|
||||||
|
"ldi r18, 0x40" "\n\t"
|
||||||
|
"std Y + 4, r18" "\n\t" // Y + 4 = USART.STATUS - clear TXC
|
||||||
|
"std Y + 2, r24" "\n\t" // Y + 2 = USART.TXDATAL - write char
|
||||||
|
"subi r25, 0xFF" "\n\t" // txtail +1
|
||||||
|
#if SERIAL_TX_BUFFER_SIZE == 256
|
||||||
|
// // No action needed to wrap the tail around -
|
||||||
|
#elif SERIAL_TX_BUFFER_SIZE == 128
|
||||||
|
"andi r25, 0x7F" "\n\t" // Wrap the tail around
|
||||||
|
#elif SERIAL_TX_BUFFER_SIZE == 64
|
||||||
|
"andi r25, 0x3F" "\n\t" // Wrap the tail around
|
||||||
|
#elif SERIAL_TX_BUFFER_SIZE == 32
|
||||||
|
"andi r25, 0x1F" "\n\t" // Wrap the tail around
|
||||||
|
#elif SERIAL_TX_BUFFER_SIZE == 16
|
||||||
|
"andi r25, 0x0F" "\n\t" // Wrap the tail around
|
||||||
|
#endif
|
||||||
|
"ldd r24, Y + 5" "\n\t" // Y + 5 = USART.CTRLA - get CTRLA into r24
|
||||||
|
"ldd r18, Z + 15" "\n\t" // txhead into r18
|
||||||
|
"cpse r18, r25" "\n\t" // if they're the same
|
||||||
|
"rjmp _done_dre_irq" "\n\t"
|
||||||
|
"andi r24, 0xDF" "\n\t" // DREIE off
|
||||||
|
"std Y + 5, r24" "\n\t" // write new ctrla
|
||||||
|
"_done_dre_irq:" "\n\t" // Beginning of the end of DRE
|
||||||
|
"std Z + 16, r25" "\n\t" // store new tail
|
||||||
|
"pop r29" "\n\t" // pop Y
|
||||||
|
"pop r28" "\n\t" // finish popping Y
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"brts .+4" "\n\t" // hop over the next insn if T bit set, means entered through do_dre, rather than poll_dre
|
||||||
|
"jmp _poll_dre_done" "\n\t" // >8k parts must us jmp, otherwise it will give PCREL error.
|
||||||
|
#else
|
||||||
|
"brts .+2" "\n\t" // hop over the next insn if T bit set, means entered through do_dre, rather than poll_dre
|
||||||
|
"rjmp _poll_dre_done" "\n\t" // 8k parts can use RJMP
|
||||||
|
#endif
|
||||||
|
"pop r27" "\n\t" // and continue with popping registers. 21 clocks left
|
||||||
|
"pop r26" "\n\t"
|
||||||
|
"pop r25" "\n\t"
|
||||||
|
"pop r24" "\n\t"
|
||||||
|
"pop r18" "\n\t" // pop SREG value from stack
|
||||||
|
"out 0x3f, r18" "\n\t" // restore SREG
|
||||||
|
"pop r18" "\n\t" // pop old r18
|
||||||
|
"pop r31" "\n\t" // pop the Z that the isr pushed.
|
||||||
|
"pop r30" "\n\t"
|
||||||
|
"reti" "\n" // and RETI!
|
||||||
|
::);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#elif USE_ASM_DRE == 1
|
||||||
|
#warning "USE_ASM_DRE == 1, but the buffer sizes are not supported, falling back to the classical DRE."
|
||||||
|
#else
|
||||||
|
void HardwareSerial::_tx_data_empty_irq(HardwareSerial& HardwareSerial) {
|
||||||
|
USART_t* usartModule = (USART_t*)HardwareSerial._hwserial_module; // reduces size a little bit
|
||||||
|
tx_buffer_index_t txTail = HardwareSerial._tx_buffer_tail;
|
||||||
|
|
||||||
|
// Check if tx buffer already empty. when called by _poll_tx_data_empty()
|
||||||
|
// if (HardwareSerial._tx_buffer_head == txTail) {
|
||||||
|
// Buffer empty, so disable "data register empty" interrupt
|
||||||
|
// usartModule->CTRLA &= (~USART_DREIE_bm);
|
||||||
|
// return;
|
||||||
|
//} // moved to poll function to make ISR smaller and faster
|
||||||
|
|
||||||
|
// There must be more data in the output
|
||||||
|
// buffer. Send the next byte
|
||||||
|
uint8_t c = HardwareSerial._tx_buffer[txTail];
|
||||||
|
|
||||||
|
// clear the TXCIF flag -- "can be cleared by writing a one to its bit
|
||||||
|
// location". This makes sure flush() won't return until the bytes
|
||||||
|
// actually got written. It is critical to do this BEFORE we write the next byte
|
||||||
|
usartModule->STATUS = USART_TXCIF_bm;
|
||||||
|
usartModule->TXDATAL = c;
|
||||||
|
|
||||||
|
txTail = (txTail + 1) & (SERIAL_TX_BUFFER_SIZE - 1); //% SERIAL_TX_BUFFER_SIZE;
|
||||||
|
uint8_t ctrla = usartModule->CTRLA;
|
||||||
|
if (HardwareSerial._tx_buffer_head == txTail) {
|
||||||
|
// Buffer empty, so disable "data register empty" interrupt
|
||||||
|
ctrla &= ~(USART_DREIE_bm);
|
||||||
|
usartModule->CTRLA = ctrla;
|
||||||
|
}
|
||||||
|
HardwareSerial._tx_buffer_tail = txTail;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// To invoke data empty "interrupt" via a call, use this method
|
||||||
|
void HardwareSerial::_poll_tx_data_empty(void) {
|
||||||
|
if ((!(SREG & CPU_I_bm)) || CPUINT.STATUS) {
|
||||||
|
// We're here because we're waiting for space in the buffer *or* we're in flush
|
||||||
|
// and waiting for the last byte to leave, yet we're either in an ISR, or
|
||||||
|
// interrupts are disabled so the ISR can't fire on it's own.
|
||||||
|
//
|
||||||
|
// Interrupts are disabled either globally or for data register empty,
|
||||||
|
// or we are in another ISR. (It doesn't matter *which* ISR we are in
|
||||||
|
// whether it's another level 0, the priority one, or heaven help us
|
||||||
|
// the NMI, if the user code says to print something or flush the buffer
|
||||||
|
// we might as well do it. It is entirely plausible that an NMI might
|
||||||
|
// attempt to print out some sort of record of what happened.
|
||||||
|
//
|
||||||
|
// so we'll have to poll the "data register empty" flag ourselves.
|
||||||
|
// If it is set, pretend an interrupt has happened and call the handler
|
||||||
|
// to free up space for us.
|
||||||
|
// -Spence 10/23/20
|
||||||
|
// Invoke interrupt handler only if conditions data register is empty
|
||||||
|
if ((*_hwserial_module).STATUS & USART_DREIF_bm) {
|
||||||
|
if (_tx_buffer_head == _tx_buffer_tail) {
|
||||||
|
// Buffer empty, so disable "data register empty" interrupt
|
||||||
|
(*_hwserial_module).CTRLA &= (~USART_DREIE_bm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if !(USE_ASM_DRE == 1 && (SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) && \
|
||||||
|
(SERIAL_TX_BUFFER_SIZE == 256 || SERIAL_TX_BUFFER_SIZE == 128 || SERIAL_TX_BUFFER_SIZE == 64 || SERIAL_TX_BUFFER_SIZE == 32 || SERIAL_TX_BUFFER_SIZE == 16))
|
||||||
|
_tx_data_empty_irq(*this);
|
||||||
|
#else // We're using ASM DRE
|
||||||
|
#ifdef USART1
|
||||||
|
void * thisSerial = this;
|
||||||
|
#endif
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"clt" "\n\t" // Clear the T flag to signal to the ISR that we got there from here. This is safe per the ABI - The T-flag can be treated like R0
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _poll_dre" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _poll_dre" "\n\t"
|
||||||
|
#endif
|
||||||
|
"_poll_dre_done:" "\n"
|
||||||
|
#ifdef USART1
|
||||||
|
::"z"((uint16_t)thisSerial)
|
||||||
|
#else
|
||||||
|
::"z"(&Serial0)
|
||||||
|
#endif
|
||||||
|
: "r18","r19","r24","r25","r26","r27"); // these got saved and restored in the ISR context, but here we don't need top and in many cases no action is needed.
|
||||||
|
// the Y pointer was already handled, because as a call-saved register, it would always need to be saved and restored, so we save 4 words of flash by doing that after
|
||||||
|
// jumps into the middle of the ISR, and before it jumps back here.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// In case interrupts are enabled, the interrupt routine will be invoked by itself
|
||||||
|
// Note that this currently does not handle cases where the DRE interruopt becomes
|
||||||
|
// disabled, yet you are actually attempting to send. I don't think it can happen.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*### # # #### # ### ### # # #### ##### # # ### #### ###
|
||||||
|
# # # # # # # # # ## ## # # # # # # # # #
|
||||||
|
#### # # #### # # # # # # ### # ##### # # # # ###
|
||||||
|
# # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### #### #### ### ### # # #### # # # ### #### ##*/
|
||||||
|
|
||||||
|
// Invoke this function before 'begin' to define the pins used
|
||||||
|
bool HardwareSerial::pins(uint8_t tx, uint8_t rx) {
|
||||||
|
uint8_t ret_val = _pins_to_swap(_module_number, tx, rx); // return 127 when correct swap number wasn't found
|
||||||
|
return swap(ret_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HardwareSerial::_pins_to_swap(uint8_t port_num, uint8_t tx_pin, uint8_t rx_pin) {
|
||||||
|
if (tx_pin == NOT_A_PIN && rx_pin == NOT_A_PIN) {
|
||||||
|
return 128; // get MUX_NONE
|
||||||
|
} else {
|
||||||
|
const uint8_t * muxtab_ptr = _usart_pins[port_num];
|
||||||
|
if (*muxtab_ptr == tx_pin && (*(muxtab_ptr + 1) == rx_pin)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#if !defined(__AVR_ATtinyx24__)
|
||||||
|
if ((*(muxtab_ptr + 4)) == tx_pin && (*(muxtab_ptr + 5) == rx_pin))
|
||||||
|
#else
|
||||||
|
if (port_num && (*(muxtab_ptr + 4)) == tx_pin && (*(muxtab_ptr + 5) == rx_pin))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return NOT_A_MUX; // At this point, we have checked all group codes for this peripheral. It ain't there. Return NOT_A_MUX.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HardwareSerial::getPin(uint8_t pin) {
|
||||||
|
if (pin >3) return NOT_A_PIN;
|
||||||
|
return (_usart_pins[_module_number + _pin_set][pin]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HardwareSerial::swap(uint8_t newmux) {
|
||||||
|
#if !(MEGATINYCORE_SERIES == 2 && defined(__ATtinyxy4__))
|
||||||
|
// it's either a 0/1-series: They have options of 0 and 1.
|
||||||
|
// Or it's a 2-series with 20 or 24 pins. Both USARTS have option of 0 & 1.
|
||||||
|
if (newmux < 2) {
|
||||||
|
_pin_set = newmux;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// means it is a 14-pin 2-series, whose second USART doesn't have an alternate location.
|
||||||
|
if (_module_number + newmux < 2) {
|
||||||
|
_pin_set = newmux;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if MEGATINYCORE_SERIES == 2
|
||||||
|
else if (newmux == MUX_NONE) { // 128 codes for MUX_NONE
|
||||||
|
_pin_set = 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
|
_pin_set = 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::begin(unsigned long baud, uint16_t options) {
|
||||||
|
// Make sure no transmissions are ongoing and USART is disabled in case begin() is called by accident
|
||||||
|
// without first calling end()
|
||||||
|
if (_state & 1) {
|
||||||
|
this->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ctrlc = (uint8_t) options;
|
||||||
|
if (ctrlc == 0) { // see if they passed anything in low byte or SERIAL_CONFIG_VALID.
|
||||||
|
ctrlc = (uint8_t)SERIAL_8N1; // low byte of 0 could mean they want SERIAL_5N1. Or that they thought they'd
|
||||||
|
}
|
||||||
|
ctrlc &= ~0x04; // Now unset that 0x04 bit if it's set, because none of the values with it set are supported. We use that to smuggle in a "this constant was specified" for 5N1
|
||||||
|
uint8_t ctrla = (uint8_t) (options >> 8);// CTRLA will get the remains of the options high byte.
|
||||||
|
uint16_t baud_setting = 0; // at this point it should be able to reuse those 2 registers that it received options in!
|
||||||
|
uint8_t ctrlb = (~ctrla & 0xC0); // Top two bits (TXEN RXEN), inverted so they match he sense in the registers.
|
||||||
|
if (baud > F_CPU / 16) { // if this baud is too fast for non-U2X
|
||||||
|
ctrlb |= USART_RXMODE0_bm; // set the U2X bit in what will become CTRLB
|
||||||
|
baud >>= 1; // And lower the baud rate by haldf
|
||||||
|
}
|
||||||
|
baud_setting = (((4 * F_CPU) / baud)); // And now the registers that baud was passed in are done.
|
||||||
|
if (baud_setting < 64) // so set to the maximum baud rate setting.
|
||||||
|
baud_setting= 64; // set the U2X bit in what will become CTRLB
|
||||||
|
//} else if (baud < (F_CPU / 16800)) { // Baud rate is too low
|
||||||
|
// baud_setting = 65535; // minimum baud rate.'
|
||||||
|
// Baud setting done now we do the other options not in CTRLC;
|
||||||
|
if (ctrla & 0x04) { // is ODME option set?
|
||||||
|
ctrlb |= USART_ODME_bm; // set the bit in what will become CTRLB
|
||||||
|
}
|
||||||
|
// Baud setting done now we do the other options.
|
||||||
|
// that aren't in CTRLC;
|
||||||
|
ctrla &= 0x2B; // Only LBME and RS485 (both of them); will get written to CTRLA, but we leave the event bit.
|
||||||
|
if (ctrlb & USART_RXEN_bm) { // if RX is to be enabled
|
||||||
|
ctrla |= USART_RXCIE_bm; // we will want to enable the ISR.
|
||||||
|
}
|
||||||
|
uint8_t setpinmask = ctrlb & 0xC8; // ODME in bit 3, TX and RX enabled in bit 6, 7
|
||||||
|
if ((ctrla & USART_LBME_bm) && (setpinmask == 0xC8)) { // if it's open-drain and loopback, need to set state bit 2.
|
||||||
|
_state |= 2; // since that changes some behavior (RXC disabled while sending) // Now we should be able to ST _state.
|
||||||
|
setpinmask |= 0x10; // this tells _set_pins not to disturb the configuration on the RX pin.
|
||||||
|
}
|
||||||
|
if (ctrla & USART_RS485_bm) { // RS485 mode recorded here too... because we need to set
|
||||||
|
setpinmask |= 0x01; // set pin output if we need to do that. Datasheet isn't clear
|
||||||
|
}
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
volatile USART_t* MyUSART = _hwserial_module;
|
||||||
|
(*MyUSART).CTRLB = 0; // gotta disable first - some things are enable-locked.
|
||||||
|
(*MyUSART).CTRLC = ctrlc; // No reason not to set first.
|
||||||
|
(*MyUSART).BAUD = baud_setting; // Wish I could have set it long ago
|
||||||
|
if (ctrla & 0x20) { // Now we have to do a bit of work
|
||||||
|
setpinmask &= 0x7F; // Remove the RX pin in this case because we get the input from elsewhere.
|
||||||
|
(*MyUSART).EVCTRL = 1; // enable event input - not clear from datasheet what's needed to
|
||||||
|
(*MyUSART).TXPLCTRL = 0xFF; // Disable pulse length encoding.
|
||||||
|
} else {
|
||||||
|
(*MyUSART).EVCTRL = 0; // This needs to be turned off when not in use.
|
||||||
|
} // finally strip out the SERIAL_EVENT_RX bit which is in the DREIE
|
||||||
|
(*MyUSART).CTRLA = ctrla & 0xDF; // position, which we never set in begin.
|
||||||
|
(*MyUSART).CTRLB = ctrlb; // Set the all important CTRLB...
|
||||||
|
_set_pins(_module_number, _pin_set, setpinmask); // set up the pin(s)
|
||||||
|
SREG = oldSREG; // re-enable interrupts, and we're done.
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::_set_pins(uint8_t mod_nbr, uint8_t mux_set, uint8_t enmask) {
|
||||||
|
// Set the mux register
|
||||||
|
#if defined(PORTMUX_USARTROUTEA)
|
||||||
|
uint8_t muxregval = PORTMUX.USARTROUTEA;
|
||||||
|
muxregval &= ~(mod_nbr ? 0x0C : 0x03);
|
||||||
|
PORTMUX.USARTROUTEA = (muxregval) | (mux_set << (mod_nbr ? 2 : 0)); // shift muxset left if needed.
|
||||||
|
|
||||||
|
#else
|
||||||
|
if (mux_set) {
|
||||||
|
PORTMUX.CTRLB |= 0x01; // for 0/1-series this can only be zero or 1
|
||||||
|
} else {
|
||||||
|
PORTMUX.CTRLB &= 0xFE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if MEGATINYCORE_SERIES == 2
|
||||||
|
if (mux_set == 3) { // not connected to pins...
|
||||||
|
return; // so we are done!
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
const uint8_t* muxrow = &(_usart_pins[mod_nbr + mux_set][0]);
|
||||||
|
if ((enmask & 0x40 && !(enmask & 0x08))) {
|
||||||
|
pinMode(muxrow[0], OUTPUT); // If and only if TX is enabled and open drain isn't should the TX interrupt be used. .
|
||||||
|
} else if (enmask & 0x50) { // if it is enabled but is in open drain mode, or is disabled, but loopback is enabled
|
||||||
|
// TX should be INPUT_PULLUP.
|
||||||
|
pinMode(muxrow[0], INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
if (enmask & 0x80 && !(enmask & 0x10)) {
|
||||||
|
// Likewise if RX is enabled, unless loopback mode is too (in which case we caught it above, it should be pulled up
|
||||||
|
pinMode(muxrow[1], INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
if (enmask & 0x01) { // finally if RS485 mode is enabled, we make XDIR output, otherwise it can't drive the pin.
|
||||||
|
pinMode(muxrow[3], OUTPUT); // make XDIR output.
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
uint8_t muxrow = mod_nbr + mux_set;
|
||||||
|
if ((enmask & 0x40 && !(enmask & 0x08))) {
|
||||||
|
pinMode(_usart_pins[muxrow][0], OUTPUT); // If any only if TX is enabled and open drain isn't should the TX pin be output.
|
||||||
|
} else if (enmask & 0x50) { // if it is enabled but is in open drain mode, or is disabled, but loopback is enabled
|
||||||
|
// TX should be INPUT_PULLUP.
|
||||||
|
pinMode(_usart_pins[muxrow][0], INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
if (enmask & 0x80 && !(enmask & 0x10)) {
|
||||||
|
// Likewise if RX is enabled, unless loopback mode is too (in which case we caught it above, it should be pulled up
|
||||||
|
pinMode(_usart_pins[muxrow][1], INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
if (enmask & 0x01) { // finally if RS485 mode is enabled, we make XDIR output, otherwise it can't drive the pin.
|
||||||
|
pinMode(_usart_pins[muxrow][3], OUTPUT); // make XDIR output.
|
||||||
|
}
|
||||||
|
// And it is up to the user to configure the XCK pin as required for their application if they are using that.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
uint8_t muxrow = mod_nbr + mux_set;
|
||||||
|
if (enmask & 0x40) { // tx enabled
|
||||||
|
pinMode(_usart_pins[muxrow][0], (enmask & 0x08) ? INPUT_PULLUP : OUTPUT);
|
||||||
|
}
|
||||||
|
if (enmask & 0x80) {
|
||||||
|
if (!(enmask & 0x10)) {
|
||||||
|
|
||||||
|
pinMode(_usart_pins[muxrow][1], INPUT_PULLUP);
|
||||||
|
} else if (!(enmask & 0x40)) { // Loopback mode set, TX disabled, and RX enabled. Wacky configuration, but I guess that means TX should be INPUT_PULLUP.
|
||||||
|
pinMode(_usart_pins[muxrow][0], INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enmask & 0x01) { // RS485 enabled
|
||||||
|
pinMode(_usart_pins[muxrow][3], OUTPUT); // make XDIR output.
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::end() {
|
||||||
|
// wait for transmission of outgoing data
|
||||||
|
flush();
|
||||||
|
// Disable receiver and transmitter as well as the RX complete and the data register empty interrupts.
|
||||||
|
// TXCIE only used in half duplex - we can just turn the damned thing off yo!
|
||||||
|
volatile USART_t * temp = _hwserial_module; /* compiler does a slightly better job with this. */
|
||||||
|
temp -> CTRLB = 0; //~(USART_RXEN_bm | USART_TXEN_bm);
|
||||||
|
temp -> CTRLA = 0; //~(USART_RXCIE_bm | USART_DREIE_bm | USART_TXCIE_bm);
|
||||||
|
temp -> STATUS = USART_TXCIF_bm | USART_RXCIF_bm; // want to make sure no chance of that firing in error now that the USART is off. TXCIE only used in half duplex
|
||||||
|
// clear any received data
|
||||||
|
_rx_buffer_head = _rx_buffer_tail;
|
||||||
|
|
||||||
|
// Note: Does not change output pins
|
||||||
|
// though the datasheetsays turning the TX module off sets it to input.
|
||||||
|
_state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HardwareSerial::available(void) {
|
||||||
|
return ((unsigned int)(SERIAL_RX_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail)) & (SERIAL_RX_BUFFER_SIZE - 1); //% SERIAL_RX_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HardwareSerial::peek(void) {
|
||||||
|
if (_rx_buffer_head == _rx_buffer_tail) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return _rx_buffer[_rx_buffer_tail];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int HardwareSerial::read(void) {
|
||||||
|
// if the head isn't ahead of the tail, we don't have any characters
|
||||||
|
if (_rx_buffer_head == _rx_buffer_tail) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
unsigned char c = _rx_buffer[_rx_buffer_tail];
|
||||||
|
_rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) & (SERIAL_RX_BUFFER_SIZE - 1); // % SERIAL_RX_BUFFER_SIZE;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int HardwareSerial::availableForWrite(void) {
|
||||||
|
tx_buffer_index_t head;
|
||||||
|
tx_buffer_index_t tail;
|
||||||
|
|
||||||
|
TX_BUFFER_ATOMIC {
|
||||||
|
head = _tx_buffer_head;
|
||||||
|
tail = _tx_buffer_tail;
|
||||||
|
}
|
||||||
|
if (head >= tail) {
|
||||||
|
return SERIAL_TX_BUFFER_SIZE - 1 - head + tail;
|
||||||
|
}
|
||||||
|
return tail - head - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::flush() {
|
||||||
|
// If we have never written a byte, no need to flush. This special
|
||||||
|
// case is needed since there is no way to force the TXCIF (transmit
|
||||||
|
// complete) bit to 1 during initialization
|
||||||
|
if (!(_state & 1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we are inside an ISR already (e.g. connected to a different peripheral then UART), in which case the UART ISRs will not be called.
|
||||||
|
// Spence 10/23/20: Changed _poll_tx_data_empty() to instead call the ISR directly in this case too
|
||||||
|
// Why elevate the interrupt if we're going to go into a busywait loop checking if the interrupt is disabled and if so, check for the bit and
|
||||||
|
// manually call the ISR if the bit is set... *anyway*? Plus, in write(), this mode will be enabled upon a write of a single character from an ISR
|
||||||
|
// and will stay that way until the buffer is empty, which would mean that the fairly long and slow UART TX ISR would have priority over a
|
||||||
|
// potentially very fast interrupt that the user may have set to priority level 1. Just because a whizz-bang feature is there doesn't mean
|
||||||
|
// it's appropriate to use for applications where it has only very small benefits, and significant risk of surprising the user and causing
|
||||||
|
// breakage of code that would otherwise work. Finally, the previous implementation didn't check if it was called from the current lvl1 ISR
|
||||||
|
// and in that case flush(), and write() with full buffer would just straight up hang...
|
||||||
|
|
||||||
|
// Spin until the data-register-empty-interrupt is disabled and TX complete interrupt flag is raised
|
||||||
|
while (((*_hwserial_module).CTRLA & USART_DREIE_bm) || (!((*_hwserial_module).STATUS & USART_TXCIF_bm))) {
|
||||||
|
|
||||||
|
// If interrupts are globally disabled or the and DR empty interrupt is disabled,
|
||||||
|
// poll the "data register empty" interrupt flag to prevent deadlock
|
||||||
|
|
||||||
|
_poll_tx_data_empty();
|
||||||
|
}
|
||||||
|
// When we get here, nothing is queued anymore (DREIE is disabled) and
|
||||||
|
// the hardware finished transmission (TXCIF is set).
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t HardwareSerial::write(uint8_t c) {
|
||||||
|
_state |= 1; // Record that we have written to serial since it was begun.
|
||||||
|
// If the buffer and the data register is empty, just write the byte
|
||||||
|
// to the data register and be done. This shortcut helps
|
||||||
|
// significantly improve the effective data rate at high (>
|
||||||
|
// 500kbit/s) bit rates, where interrupt overhead becomes a slowdown.
|
||||||
|
if ((_tx_buffer_head == _tx_buffer_tail) && ((*_hwserial_module).STATUS & USART_DREIF_bm)) {
|
||||||
|
if (_state & 2) { // in half duplex mode, we turn off RXC interrupt
|
||||||
|
uint8_t ctrla = (*_hwserial_module).CTRLA;
|
||||||
|
ctrla &= ~USART_RXCIE_bm;
|
||||||
|
ctrla |= USART_TXCIE_bm;
|
||||||
|
(*_hwserial_module).STATUS = USART_TXCIF_bm;
|
||||||
|
(*_hwserial_module).CTRLA = ctrla;
|
||||||
|
} else {
|
||||||
|
(*_hwserial_module).STATUS = USART_TXCIF_bm;
|
||||||
|
}
|
||||||
|
// MUST clear TXCIF **before** writing new char, otherwise ill-timed interrupt can cause it to erase the flag after the new charchter has been sent!
|
||||||
|
(*_hwserial_module).TXDATAL = c;
|
||||||
|
|
||||||
|
/* I cannot figure out *HOW* the DRE could be enabled at this point (buffer empty and DRE flag up)
|
||||||
|
* When the buffer was emptied, it would have turned off the DREI after it loaded the last byte.
|
||||||
|
* Thus, the only possible way this could happen is if an interrupt also tried to write to serial,
|
||||||
|
* *immediately* after we checked that the buffer was empty, before we made it not empty. And
|
||||||
|
* in that case, without this line it would lose one of the characters... with that line, it could
|
||||||
|
* stop servicing DRE until another serial write, AND lose a character. That's not better! -Spence 4/2021
|
||||||
|
* So this is to stop a race condition in which people are doing something that every guide everywhere says not to do
|
||||||
|
* (writing serial from within an ISR).
|
||||||
|
* I maintain that users SHOULD NOT WRITE TO SERIAL FROM AN ISR, and certainly not while the non-interrupt code is also writing!
|
||||||
|
* Original comments:
|
||||||
|
* // Make sure data register empty interrupt is disabled to avoid
|
||||||
|
* // that the interrupt handler is called in this situation
|
||||||
|
* (*_hwserial_module).CTRLA &= (~USART_DREIE_bm);
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
tx_buffer_index_t i = (_tx_buffer_head + 1) & (SERIAL_TX_BUFFER_SIZE - 1); // % SERIAL_TX_BUFFER_SIZE;
|
||||||
|
|
||||||
|
// If the output buffer is full, there's nothing we can do other than to
|
||||||
|
// wait for the interrupt handler to empty it a bit (or emulate interrupts)
|
||||||
|
while (i == _tx_buffer_tail) {
|
||||||
|
_poll_tx_data_empty();
|
||||||
|
}
|
||||||
|
_tx_buffer[_tx_buffer_head] = c;
|
||||||
|
_tx_buffer_head = i;
|
||||||
|
if (_state & 2) { // in half duplex mode, we turn off RXC interrupt
|
||||||
|
uint8_t ctrla = (*_hwserial_module).CTRLA;
|
||||||
|
ctrla &= ~USART_RXCIE_bm;
|
||||||
|
ctrla |= USART_TXCIE_bm | USART_DREIE_bm;
|
||||||
|
(*_hwserial_module).STATUS = USART_TXCIF_bm;
|
||||||
|
(*_hwserial_module).CTRLA = ctrla;
|
||||||
|
} else {
|
||||||
|
// Enable "data register empty interrupt"
|
||||||
|
|
||||||
|
(*_hwserial_module).CTRLA |= USART_DREIE_bm;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::printHex(const uint8_t b) {
|
||||||
|
char x = (b >> 4) | '0';
|
||||||
|
if (x > '9')
|
||||||
|
x += 7;
|
||||||
|
write(x);
|
||||||
|
x = (b & 0x0F) | '0';
|
||||||
|
if (x > '9')
|
||||||
|
x += 7;
|
||||||
|
write(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::printHex(const uint16_t w, bool swaporder) {
|
||||||
|
uint8_t *ptr = (uint8_t *) &w;
|
||||||
|
if (swaporder) {
|
||||||
|
printHex(*(ptr++));
|
||||||
|
printHex(*(ptr));
|
||||||
|
} else {
|
||||||
|
printHex(*(ptr + 1));
|
||||||
|
printHex(*(ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::printHex(const uint32_t l, bool swaporder) {
|
||||||
|
uint8_t *ptr = (uint8_t *) &l;
|
||||||
|
if (swaporder) {
|
||||||
|
printHex(*(ptr++));
|
||||||
|
printHex(*(ptr++));
|
||||||
|
printHex(*(ptr++));
|
||||||
|
printHex(*(ptr));
|
||||||
|
} else {
|
||||||
|
ptr+=3;
|
||||||
|
printHex(*(ptr--));
|
||||||
|
printHex(*(ptr--));
|
||||||
|
printHex(*(ptr--));
|
||||||
|
printHex(*(ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * HardwareSerial::printHex(uint8_t* p, uint8_t len, char sep) {
|
||||||
|
for (byte i = 0; i < len; i++) {
|
||||||
|
if (sep && i) write(sep);
|
||||||
|
printHex(*p++);
|
||||||
|
}
|
||||||
|
println();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t * HardwareSerial::printHex(uint16_t* p, uint8_t len, char sep, bool swaporder) {
|
||||||
|
for (byte i = 0; i < len; i++) {
|
||||||
|
if (sep && i) write(sep);
|
||||||
|
printHex(*p++, swaporder);
|
||||||
|
}
|
||||||
|
println();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
volatile uint8_t * HardwareSerial::printHex(volatile uint8_t* p, uint8_t len, char sep) {
|
||||||
|
for (byte i = 0; i < len; i++) {
|
||||||
|
if (sep && i) write(sep);
|
||||||
|
uint8_t t = *p++;
|
||||||
|
printHex(t);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
volatile uint16_t * HardwareSerial::printHex(volatile uint16_t* p, uint8_t len, char sep, bool swaporder) {
|
||||||
|
for (byte i = 0; i < len; i++) {
|
||||||
|
if (sep && i) write(sep);
|
||||||
|
uint16_t t = *p++;
|
||||||
|
printHex(t, swaporder);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,7 @@
|
||||||
|
// We are returning to the conventional name and there is no longer a bloat inducing class layer between
|
||||||
|
|
||||||
|
#ifndef HWSERIALNAME_HACK_H
|
||||||
|
#define HWSERIALNAME_HACK_H
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#define UartClass HardwareSerial
|
||||||
|
#endif
|
|
@ -0,0 +1,131 @@
|
||||||
|
/* UART0.cpp - Hardware serial library for USART0
|
||||||
|
* This library is free software released under LGPL 2.1.
|
||||||
|
* See License.md for more information.
|
||||||
|
* This file is part of megaTinyCore.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 Nicholas Zambetti, Modified by
|
||||||
|
* 11/23/2006 David A. Mellis, 9/20/2010 Mark Sproul,
|
||||||
|
* 8/24/2012 Alarus, 12/3/2013 Matthijs Kooijman
|
||||||
|
* unknown others 2013-2020, 2020-2022 Spence Konde
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Each HardwareSerial is defined in its own file, sine the linker pulls
|
||||||
|
* in the entire file when any element inside is used. --gc-sections can
|
||||||
|
* additionally cause unused symbols to be dropped, but ISRs have the
|
||||||
|
* "used" attribute so are never dropped and they keep the
|
||||||
|
* HardwareSerial instance in as well. Putting each instance in its own
|
||||||
|
* file prevents the linker from pulling in any unused instances in the
|
||||||
|
* first place.
|
||||||
|
* There are now two versions of each ISR. All versions are stubs to call
|
||||||
|
* the real code (so it is not duplicated). The ASM versions are more
|
||||||
|
* efficient than calling normally can ever be because they completely
|
||||||
|
* ignore the ABI rules for the transition from duplicated to shared
|
||||||
|
* code and call it the most effcieient way possiblem so the almost all of
|
||||||
|
* the flash overhead is in the shared section. They'll stop working if
|
||||||
|
* anything changes, and it's not entirely clear whether the trick of
|
||||||
|
* dropping out of assembly while naked to grab the address is legal,
|
||||||
|
* though there's no reason it shouldn't be.
|
||||||
|
* The assembly implementations over in UART.cpp depends on the structure
|
||||||
|
* of the SerialClass. Any changes to the class member variables will
|
||||||
|
* require changes to the asm to match.
|
||||||
|
* This was done for 1.5.12 to correct a serious defect. amd fix a
|
||||||
|
* to make things work better with millis off, and free up 4 bytes of sram.
|
||||||
|
*/
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "UART.h"
|
||||||
|
#include "UART_private.h"
|
||||||
|
|
||||||
|
#if defined(USART0)
|
||||||
|
#if defined(USE_ASM_TXC) && USE_ASM_TXC == 1 //&& defined(USART1) // No benefit to this if it's just one USART
|
||||||
|
// Note the difference between this and the other ISRs - here we don't care at all about the serial object, we just have to work with the USART
|
||||||
|
ISR(USART0_TXC_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t" // push the low byte of Z - we start out 5-6 clocks behind the ball, these three instructions take 4-5 -> 9 or 11 by th time we reach _do_txc
|
||||||
|
"ldi r30, 0x00" "\n\t" // and put the low bit of this USART there - 0x20 * n
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _do_txc" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _do_txc" "\n\t"
|
||||||
|
#endif // _do_txc pushes the other necessary registers and loads 0x08 into the high byte.
|
||||||
|
// The reason this is possible here and not elsewhere is because TXC only needs the USART, while the others need the HardwareSerial instance.
|
||||||
|
:::);
|
||||||
|
}
|
||||||
|
#elif defined(USE_ASM_TXC) && USE_ASM_TXC == 2
|
||||||
|
ISR(USART0_TXC_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t"
|
||||||
|
"push r31" "\n\t"
|
||||||
|
:::);
|
||||||
|
__asm__ __volatile__(
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _do_txc" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _do_txc" "\n\t"
|
||||||
|
#endif
|
||||||
|
::"z"(&Serial0));
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
ISR(USART0_TXC_vect) {
|
||||||
|
// only enabled in half duplex mode - we disable RX interrupt while sending.
|
||||||
|
// When we are done sending, we re-enable the RX interrupt and disable this one.
|
||||||
|
// Note that we do NOT clear TXC flag, which the flush() method relies on.
|
||||||
|
uint8_t ctrla;
|
||||||
|
while (USART0.STATUS & USART_RXCIF_bm) {
|
||||||
|
ctrla = USART0.RXDATAL; // We sent these, so dump them, using local var as trashcan.
|
||||||
|
}
|
||||||
|
ctrla = USART0.CTRLA; // Get current CTRLA
|
||||||
|
ctrla |= USART_RXCIE_bm; // turn on receive complete
|
||||||
|
ctrla &= ~USART_TXCIE_bm; // turn off transmit complete
|
||||||
|
USART0.CTRLA = ctrla; // Write it back to CTRLA.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(defined(USE_ASM_RXC) && (USE_ASM_RXC == 1 || USE_ASM_RXC == 2) && (SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) /* && defined(USART1)*/)
|
||||||
|
ISR(USART0_RXC_vect) {
|
||||||
|
HardwareSerial::_rx_complete_irq(Serial);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ISR(USART0_RXC_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t" //we start out 5-6 clocks behind the ball, then do 2 push + 2 ldi + 2-3 for jump = 11 or 13 clocks to _do_rxc (and dre is the same)
|
||||||
|
"push r31" "\n\t"
|
||||||
|
"cbi 0x1F, 0" "\n\t"
|
||||||
|
:::);
|
||||||
|
__asm__ __volatile__(
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _do_rxc" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _do_rxc" "\n\t"
|
||||||
|
#endif
|
||||||
|
::"z"(&Serial0));
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if !(defined(USE_ASM_DRE) && USE_ASM_DRE == 1 && \
|
||||||
|
(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) && \
|
||||||
|
(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_TX_BUFFER_SIZE == 128 || SERIAL_TX_BUFFER_SIZE == 64 || SERIAL_TX_BUFFER_SIZE == 32 || SERIAL_TX_BUFFER_SIZE == 16))
|
||||||
|
ISR(USART0_DRE_vect) {
|
||||||
|
HardwareSerial::_tx_data_empty_irq(Serial);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ISR(USART0_DRE_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t"
|
||||||
|
"push r31" "\n\t"
|
||||||
|
"cbi 0x1F, 0" "\n\t"
|
||||||
|
:::);
|
||||||
|
__asm__ __volatile__(
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _do_dre" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _do_dre" "\n\t"
|
||||||
|
#endif
|
||||||
|
::"z"(&Serial0));
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
HardwareSerial Serial0(&USART0, 0, HWSERIAL0_MUX_DEFAULT); //Elsewhere we define an alias of Serial for this :-)
|
||||||
|
#endif
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* UART1.cpp - Hardware serial library for USART1
|
||||||
|
* This library is free software released under LGPL 2.1.
|
||||||
|
* See License.md for more information.
|
||||||
|
* This file is part of megaTinyCore.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 Nicholas Zambetti, Modified by
|
||||||
|
* 11/23/2006 David A. Mellis, 9/20/2010 Mark Sproul,
|
||||||
|
* 8/24/2012 Alarus, 12/3/2013 Matthijs Kooijman
|
||||||
|
* Others (unknown) 2013-2017, 2017-2021 Spence Konde
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "UART.h"
|
||||||
|
#include "UART_private.h"
|
||||||
|
|
||||||
|
#if defined(USART1)
|
||||||
|
#if USE_ASM_TXC == 1
|
||||||
|
ISR(USART1_TXC_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t" // push the low byte of Z
|
||||||
|
"ldi r30, 0x20" "\n\t" // and put the low bit of this USART there - 0x20 * n
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _do_txc" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _do_txc" "\n\t"
|
||||||
|
#endif // _do_txc pushes the other necessary registers and loads 0x08 into the high byte.
|
||||||
|
:::);
|
||||||
|
}
|
||||||
|
#elif USE_ASM_TXC == 2
|
||||||
|
ISR(USART1_TXC_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t"
|
||||||
|
"push r31" "\n\t"
|
||||||
|
:::);
|
||||||
|
__asm__ __volatile__(
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _do_txc" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _do_txc" "\n\t"
|
||||||
|
#endif
|
||||||
|
::"z"(&Serial1));
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
ISR(USART1_TXC_vect) {
|
||||||
|
uint8_t ctrla;
|
||||||
|
while (USART1.STATUS & USART_RXCIF_bm) {
|
||||||
|
ctrla = USART1.RXDATAL;
|
||||||
|
}
|
||||||
|
ctrla = USART1.CTRLA;
|
||||||
|
ctrla |= USART_RXCIE_bm; // turn on receive complete
|
||||||
|
ctrla &= ~USART_TXCIE_bm; // turn off transmit complete
|
||||||
|
USART1.CTRLA = ctrla;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(defined(USE_ASM_RXC) && (USE_ASM_RXC == 1 || USE_ASM_RXC == 2) && (SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) /* && defined(USART1)*/)
|
||||||
|
ISR(USART1_RXC_vect) {
|
||||||
|
HardwareSerial::_rx_complete_irq(Serial1);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ISR(USART1_RXC_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t"
|
||||||
|
"push r31" "\n\t"
|
||||||
|
"sbi 0x1F, 0" "\n\t"
|
||||||
|
:::);
|
||||||
|
__asm__ __volatile__(
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _do_rxc" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _do_rxc" "\n\t"
|
||||||
|
#endif
|
||||||
|
::"z"(&Serial1));
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if !(defined(USE_ASM_DRE) && USE_ASM_DRE == 1 && \
|
||||||
|
(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) && \
|
||||||
|
(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_TX_BUFFER_SIZE == 128 || SERIAL_TX_BUFFER_SIZE == 64 || SERIAL_TX_BUFFER_SIZE == 32 || SERIAL_TX_BUFFER_SIZE == 16))
|
||||||
|
ISR(USART1_DRE_vect) {
|
||||||
|
HardwareSerial::_tx_data_empty_irq(Serial1);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ISR(USART1_DRE_vect, ISR_NAKED) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"push r30" "\n\t"
|
||||||
|
"push r31" "\n\t"
|
||||||
|
"sbi 0x1F, 0" "\n\t"
|
||||||
|
:::);
|
||||||
|
__asm__ __volatile__(
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp _do_dre" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp _do_dre" "\n\t"
|
||||||
|
#endif
|
||||||
|
::"z"(&Serial1));
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
HardwareSerial Serial1(&USART1, 1, HWSERIAL1_MUX_DEFAULT);
|
||||||
|
#endif // HWSERIAL1
|
|
@ -0,0 +1,114 @@
|
||||||
|
/* UART_check_pins.h - Validate pins_arduino from the variant.
|
||||||
|
*
|
||||||
|
* Written by Spence Konde in late 2021.
|
||||||
|
*
|
||||||
|
* This file is part of DxCore, which is free software (LGPL 2.1) based on the work
|
||||||
|
* of countless members of the Arduino community.
|
||||||
|
* See License.md for more information
|
||||||
|
*
|
||||||
|
* This is a mixture of stuff that was formerly spread out, and novel code to generate a _usart_pins variable to
|
||||||
|
* hold required pin identifiers, mux values, and so on, as well as moving things that we bloody well know
|
||||||
|
* out of variants. It's not like the DRE vector for hardware serial 1 is ever going to be something other than
|
||||||
|
* USART1_DRE_vect or something, so why do we act like it could be?
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* Recreate all those old defines so they aren't spread over all the variants when they don't vary!
|
||||||
|
* The core does NOT use these, but other code might, so we should make sure they're here.
|
||||||
|
* We also pick the default as the default if it's not defined.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(USART0) && !defined(HWSERIAL0_MUX_DEFAULT)
|
||||||
|
#define HWSERIAL0_MUX_DEFAULT (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USART1) && !defined(HWSERIAL1_MUX_DEFAULT)
|
||||||
|
#define HWSERIAL1_MUX_DEFAULT (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USART1)
|
||||||
|
#define NUM_HWSERIAL_PORTS 2
|
||||||
|
#elif defined(USART0)
|
||||||
|
#define NUM_HWSERIAL_PORTS 1
|
||||||
|
#else
|
||||||
|
#error "No USARTs present. All supported parts have at least 1 - something is wrong!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USART0)
|
||||||
|
#define HAVE_HWSERIAL0 1
|
||||||
|
#define HWSERIAL0_DRE_VECTOR USART0_DRE_vect
|
||||||
|
#define HWSERIAL0_DRE_VECTOR_NUM USART0_DRE_vect_num
|
||||||
|
#define HWSERIAL0_RXC_VECTOR USART0_RXC_vect
|
||||||
|
#define HWSERIAL0 &USART0
|
||||||
|
#endif
|
||||||
|
#if defined(USART1)
|
||||||
|
#define HAVE_HWSERIAL1 1
|
||||||
|
#define HWSERIAL1_DRE_VECTOR USART1_DRE_vect
|
||||||
|
#define HWSERIAL1_DRE_VECTOR_NUM USART1_DRE_vect_num
|
||||||
|
#define HWSERIAL1_RXC_VECTOR USART1_RXC_vect
|
||||||
|
#define HWSERIAL1 &USART1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
/* Make sure the variant doesn't define non-existent pins as belonging to serial ports. */
|
||||||
|
#if defined(PIN_HWSERIAL0_TX) && (PIN_HWSERIAL0_TX >= NUM_TOTAL_PINS) && (PIN_HWSERIAL0_TX != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL0_TX is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL0_RX) && (PIN_HWSERIAL0_RX >= NUM_TOTAL_PINS) && (PIN_HWSERIAL0_RX != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL0_RX is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL0_XCK) && (PIN_HWSERIAL0_XCK >= NUM_TOTAL_PINS) && (PIN_HWSERIAL0_XCK != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL0_XCK is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL0_XDIR) && (PIN_HWSERIAL0_XDIR >= NUM_TOTAL_PINS) && (PIN_HWSERIAL0_XDIR != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL0_XDIR is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL0_TX_PINSWAP_1) && (PIN_HWSERIAL0_TX_PINSWAP_1 >= NUM_TOTAL_PINS) && (PIN_HWSERIAL0_TX_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL0_TX_PINSWAP_1 is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL0_RX_PINSWAP_1) && (PIN_HWSERIAL0_RX_PINSWAP_1 >= NUM_TOTAL_PINS) && (PIN_HWSERIAL0_RX_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL0_RX_PINSWAP_1 is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL0_XCK_PINSWAP_1) && (PIN_HWSERIAL0_XCK_PINSWAP_1 >= NUM_TOTAL_PINS) && (PIN_HWSERIAL0_XCK_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL0_XCK_PINSWAP_1 is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL0_XDIR_PINSWAP_1) && (PIN_HWSERIAL0_XDIR_PINSWAP_1 >= NUM_TOTAL_PINS) && (PIN_HWSERIAL0_XDIR_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL0_XDIR_PINSWAP_1 is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL1_TX) && (PIN_HWSERIAL1_TX >= NUM_TOTAL_PINS) && (PIN_HWSERIAL1_TX != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL1_TX is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL1_RX) && (PIN_HWSERIAL1_RX >= NUM_TOTAL_PINS) && (PIN_HWSERIAL1_RX != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL1_RX is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL1_XCK) && (PIN_HWSERIAL1_XCK >= NUM_TOTAL_PINS) && (PIN_HWSERIAL1_XCK != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL1_XCK is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL1_XDIR) && (PIN_HWSERIAL1_XDIR >= NUM_TOTAL_PINS) && (PIN_HWSERIAL1_XDIR != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL1_XDIR is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL1_TX_PINSWAP_1) && (PIN_HWSERIAL1_TX_PINSWAP_1 >= NUM_TOTAL_PINS) && (PIN_HWSERIAL1_TX_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL1_TX_PINSWAP_1 is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL1_RX_PINSWAP_1) && (PIN_HWSERIAL1_RX_PINSWAP_1 >= NUM_TOTAL_PINS) && (PIN_HWSERIAL1_RX_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL1_RX_PINSWAP_1 is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL1_XCK_PINSWAP_1) && (PIN_HWSERIAL1_XCK_PINSWAP_1 >= NUM_TOTAL_PINS) && (PIN_HWSERIAL1_XCK_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL1_XCK_PINSWAP_1 is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if defined(PIN_HWSERIAL1_XDIR_PINSWAP_1) && (PIN_HWSERIAL1_XDIR_PINSWAP_1 >= NUM_TOTAL_PINS) && (PIN_HWSERIAL1_XDIR_PINSWAP_1 != NOT_A_PIN)
|
||||||
|
#error "Variant file is not self-consistent - PIN_HWSERIAL1_XDIR_PINSWAP_1 is defined as a pin which does not exist, but not NOT_A_PIN"
|
||||||
|
#endif
|
||||||
|
#if (defined(USART1) && !((PIN_HWSERIAL1_TX != NOT_A_PIN && PIN_HWSERIAL1_RX != NOT_A_PIN)))
|
||||||
|
// if USART1 is defined, and default pins aren't NOT_A_PIN they should be the same as USART0's alternates
|
||||||
|
#if (((PIN_HWSERIAL1_TX != PIN_HWSERIAL0_TX_PINSWAP_1) || (PIN_HWSERIAL1_RX != PIN_HWSERIAL0_RX_PINSWAP_1)) || \
|
||||||
|
((PIN_HWSERIAL1_XCK != PIN_HWSERIAL0_XCK_PINSWAP_1) && (PIN_HWSERIAL1_XCK != NOT_A_PIN)) || \
|
||||||
|
((PIN_HWSERIAL1_XDIR != PIN_HWSERIAL0_XDIR_PINSWAP_1) && (PIN_HWSERIAL1_XDIR != NOT_A_PIN)))
|
||||||
|
#error "The variant file is incorrect. On all supported parts, the default USART1 pinset is identical to the alternate USART0 one."
|
||||||
|
#endif
|
||||||
|
#elif (defined(USART1) && ((PIN_HWSERIAL1_TX == NOT_A_PIN || PIN_HWSERIAL1_RX == NOT_A_PIN)))
|
||||||
|
// if USART1 is defined and the pins are NOT_A_PIN, then the variant is wrong, because there are no parts like that.
|
||||||
|
#error "The variant file is incorrect. On all supported parts w/USART1, the default TX and RX exist, variant says they're NOT_A_PIN"
|
||||||
|
#endif
|
|
@ -0,0 +1,290 @@
|
||||||
|
/* UART_constants.h - Hardware serial library constants.
|
||||||
|
* (C) Spence Konde 2021
|
||||||
|
* This library is free software released under LGPL 2.1.
|
||||||
|
* See License.md for more information.
|
||||||
|
* This file is part of DxCore and megaTinyCore
|
||||||
|
*
|
||||||
|
* These are values that get passed to the second argument of Serial.begin in
|
||||||
|
* DxCore 1.4.0+ and megaTinyCore 2.5.0+.
|
||||||
|
*/
|
||||||
|
/*"What the bloody hell are all these static casts for?!"
|
||||||
|
* Well, the io headers define them as enumerated types. From C++20 onwards mixing enums is deprecated. These static casts do not effect behavior for everyone else
|
||||||
|
* but makethe user experience significantly better for C++20 people.
|
||||||
|
* Note that C++ 20 is not supported by this core . This was done in response to a PR and has zero impact om everyone else. Changes to make life easier for users
|
||||||
|
* of unsupported versions of the standards that do not meet both of thoe criteria will not be addressed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UART_CONSTANTS_H
|
||||||
|
#define UART_CONSTANTS_H
|
||||||
|
|
||||||
|
#define SERIAL_PARITY_EVEN (static_cast<uint8_t>(USART_PMODE_EVEN_gc))
|
||||||
|
#define SERIAL_PARITY_ODD (static_cast<uint8_t>(USART_PMODE_ODD_gc))
|
||||||
|
#define SERIAL_PARITY_NONE (static_cast<uint8_t>(USART_PMODE_DISABLED_gc))
|
||||||
|
#define SERIAL_PARITY_MASK (static_cast<uint8_t>(USART_PMODE_gm))
|
||||||
|
|
||||||
|
#define SERIAL_STOP_BIT_1 (static_cast<uint8_t>(USART_SBMODE_1BIT_gc))
|
||||||
|
#define SERIAL_STOP_BIT_2 (static_cast<uint8_t>(USART_SBMODE_2BIT_gc))
|
||||||
|
#define SERIAL_STOP_BIT_MASK (static_cast<uint8_t>(USART_SBMODE_gm))
|
||||||
|
|
||||||
|
#define SERIAL_DATA_5 (static_cast<uint8_t>(USART_CHSIZE_5BIT_gc | 0x04))
|
||||||
|
// Special case - we strip out bit 2 in order to detect use of high-byte modifiers without specifying low byte, which is presumably user error.
|
||||||
|
// and default 8N1 instead of the hardware default of 5N1.
|
||||||
|
#define SERIAL_DATA_6 (static_cast<uint8_t>(USART_CHSIZE_6BIT_gc))
|
||||||
|
#define SERIAL_DATA_7 (static_cast<uint8_t>(USART_CHSIZE_7BIT_gc))
|
||||||
|
#define SERIAL_DATA_8 (static_cast<uint8_t>(USART_CHSIZE_8BIT_gc))
|
||||||
|
#define SERIAL_DATA_INVALID1 (static_cast<uint8_t>(badArg("This is a 'reserved' value and does not identify a character size"), 0x04))
|
||||||
|
#define SERIAL_DATA_INVALID2 (static_cast<uint8_t>(badArg("This is a 'reserved' value and does not identify a character size"), 0x05))
|
||||||
|
#define SERIAL_DATA_9L (static_cast<uint8_t>(badArg("9-bit serial not supported"),0x06))
|
||||||
|
#define SERIAL_DATA_9H (static_cast<uint8_t>(badArg("9-bit serial not supported"),0x07))
|
||||||
|
#define SERIAL_DATA_MASK (static_cast<uint8_t>(USART_CHSIZE_gm))
|
||||||
|
/* 9-bit is a can of worms. Aggressive ones with sharp teeth,
|
||||||
|
* hungry for soft fle-- oh, hm, it seems to be a typo, it says "flash"...
|
||||||
|
*
|
||||||
|
* It would be a nightmare to support overhead of everything is
|
||||||
|
* greatly increased because you have to switch from 8-bit to
|
||||||
|
* 16-bit types.
|
||||||
|
* Nothing uses 9-bit serial! Including our implementation.
|
||||||
|
*
|
||||||
|
* A future update may mask off 0x04 from this bitfield before writing it
|
||||||
|
* so that SERIAL_DATA_5 could be #defined as 0x04. That way, before masking it, we
|
||||||
|
* could test if it was 0, and get rid of the SERIAL_CONFIG_VALID below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SERIAL_MODE_ASYNC (static_cast<uint8_t>(USART_CMODE_ASYNCHRONOUS_gc))
|
||||||
|
#define SERIAL_MODE_MSPI (static_cast<uint8_t>(USART_CMODE_MSPI_gc))
|
||||||
|
#define SERIAL_MODE_IRCOM (static_cast<uint8_t>(USART_CMODE_IRCOM_gc))
|
||||||
|
#define SERIAL_MODE_SYNC (static_cast<uint8_t>(USART_CMODE_SYNCHRONOUS_gc))
|
||||||
|
/* MSPI doesn't use the Parity, have start or stop bits and is always 8 bit characters.
|
||||||
|
* Sync and IRCOM modes are like ASync - but only */
|
||||||
|
|
||||||
|
// Used only in MSPI mode.
|
||||||
|
#define SERIAL_MSPI_LSB (static_cast<uint8_t>(USART_UDORD_bm))
|
||||||
|
#define SERIAL_MSPI_MSB (static_cast<uint8_t>(0))
|
||||||
|
#define SERIAL_MSPI_PHASE (static_cast<uint8_t>(USART_UCPHA_bm))
|
||||||
|
/* You can combine them yourself with | - but you need 5 things:
|
||||||
|
* A SERIAL_MODE
|
||||||
|
* A SERIAL_STOP_BIT option (can be omitted for 1 SB)
|
||||||
|
* A SERIAL_PARITY option (can be omitted for none)
|
||||||
|
* A SERIAL_DATA option specifying the size of a character (Cannot be omitted, default is 5bit!)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SERIAL_5N1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_NONE | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6N1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_NONE | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7N1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_NONE | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8N1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_NONE | SERIAL_DATA_8)
|
||||||
|
#define SERIAL_5N2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_NONE | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6N2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_NONE | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7N2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_NONE | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8N2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_NONE | SERIAL_DATA_8)
|
||||||
|
#define SERIAL_5E1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_EVEN | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6E1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_EVEN | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7E1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_EVEN | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8E1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_EVEN | SERIAL_DATA_8)
|
||||||
|
#define SERIAL_5E2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_EVEN | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6E2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_EVEN | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7E2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_EVEN | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8E2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_EVEN | SERIAL_DATA_8)
|
||||||
|
#define SERIAL_5O1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_ODD | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6O1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_ODD | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7O1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_ODD | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8O1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_ODD | SERIAL_DATA_8)
|
||||||
|
#define SERIAL_5O2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_ODD | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6O2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_ODD | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7O2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_ODD | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8O2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_ODD | SERIAL_DATA_8)
|
||||||
|
|
||||||
|
#define SERIAL_MSPI_MSB_FIRST (SERIAL_MODE_MSPI | SERIAL_MSPI_MSB)
|
||||||
|
#define SERIAL_MSPI_LSB_FIRST (SERIAL_MODE_MSPI | SERIAL_MSPI_LSB)
|
||||||
|
#define SERIAL_MSPI_MSB_FIRST_PHASE (SERIAL_MODE_MSPI | SERIAL_MSPI_PHASE | SERIAL_MSPI_MSB)
|
||||||
|
#define SERIAL_MSPI_LSB_FIRST_PHASE (SERIAL_MODE_MSPI | SERIAL_MSPI_PHASE | SERIAL_MSPI_LSB)
|
||||||
|
|
||||||
|
/* helper options for syncBegin() and mspiBegin() */
|
||||||
|
#define MSPIBEGIN_INVERT (PIN_DIR_SET | PIN_INVERT_SET)
|
||||||
|
#define MSPIBEGIN_NORMAL (PIN_DIR_SET | PIN_INVERT_CLR)
|
||||||
|
#define SYNCBEGIN_INVERT_MASTER (PIN_DIR_SET | PIN_INVERT_SET)
|
||||||
|
#define SYNCBEGIN_NORMAL_MASTER (PIN_DIR_SET | PIN_INVERT_CLR)
|
||||||
|
#define SYNCBEGIN_INVERT_SLAVE (PIN_DIR_CLR | PIN_INVERT_SET)
|
||||||
|
#define SYNCBEGIN_NORMAL_SLAVE (PIN_DIR_CLR | PIN_INVERT_CLR)
|
||||||
|
/* helper defines to get pins currently selected by number */
|
||||||
|
#define PIN_SERIAL_TX (0)
|
||||||
|
#define PIN_SERIAL_RX (1)
|
||||||
|
#define PIN_SERIAL_XCK (2)
|
||||||
|
#define PIN_SERIAL_XDIR (3)
|
||||||
|
|
||||||
|
/* Modifier Definitions - these can be OR'ed with the other definition to turn on features like one-wire half duplex and more */
|
||||||
|
#if !defined(USART_RS485_0_bm)
|
||||||
|
#define USART_RS485_0_bm USART_RS4850_bm
|
||||||
|
#if defined(USART_RS4851_bm)
|
||||||
|
#define USART_RS485_1_bm USART_RS4851_bm
|
||||||
|
#endif
|
||||||
|
#elif !defined(USART_RS4850_bm)
|
||||||
|
#define USART_RS4850_bm USART_RS485_0_bm
|
||||||
|
#if defined(USART_RS485_1_bm)
|
||||||
|
#define USART_RS4851_bm USART_RS485_1_bm
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#define SERIAL_RS485 (((uint16_t) USART_RS485_0_bm) << 8)// 0x0100
|
||||||
|
#if defined(USART_RS485_1_bm)
|
||||||
|
#define SERIAL_RS485_OTHER (((uint16_t) USART_RS485_1_bm) << 9)// 0x0200 tinyAVR 0/1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SERIAL_OPENDRAIN ((uint16_t) 0x0400)// 0x0400
|
||||||
|
#define SERIAL_LOOPBACK (((uint16_t) USART_LBME_bm) << 8)// 0x0800
|
||||||
|
#define SERIAL_TX_ONLY (((uint16_t) USART_RXEN_bm) << 8)// 0x8000 The TXEN/RXEN bits are swapped - we invert the meaning of this bit.
|
||||||
|
#define SERIAL_RX_ONLY (((uint16_t) USART_TXEN_bm) << 8)// 0x4000 so if not specified, you get a serial port with both pins. Do not specify both. That will not enable anything.
|
||||||
|
#define SERIAL_EVENT_RX ((uint16_t) 0x2000)// 0x2000
|
||||||
|
//#define SERIAL_MODE_SYNC Defined Above // 0x0040 - works much like a modifier to enable synchronous mode.
|
||||||
|
// See the Serial reference for more information as additional steps are required
|
||||||
|
#define SERIAL_HALF_DUPLEX (SERIAL_LOOPBACK | SERIAL_OPENDRAIN)
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SERIAL_AUTOBAUD (0x80000000) // OR with baud rate for topology 3 in Ref. Serial
|
||||||
|
#define SERIAL_REQUIRE_AUTOBAUD (0xFFFFFFFF) // Specify autobaud... plus an obscenely fast baud rate. The other device must send a sync frame. Good for slaves in topology 2, or in topology 1
|
||||||
|
#define SERIAL_MAKE_AUTOBAUD(intial_baud) ((__asm__ __volatile__ ("ori %D0, 0x80" "\n\t" : "+d" (uint32_t)(initial_baud))), initial_baud) // Like ORing the baud rate, only faster, if it works;
|
||||||
|
|
||||||
|
#define SERIAL_AUTOBAUD_DISABLED 0x00
|
||||||
|
|
||||||
|
/* Cumulative generally negative statii */
|
||||||
|
#define SERIAL_WRITTEN 0x01 // Indicates that one or more character has been written to this serial port since begin called, used internally
|
||||||
|
#define SERIAL_HALF_DUPLEX_ENABLED 0x02 // Indicates we are in half duplex mode. Used internally to control when interrupts are switched
|
||||||
|
#define SERIAL_PARITY_ERROR 0x04 // Indicates that since getStatus() was last called there has been one or more parity error. Parity error characters are not retained.
|
||||||
|
#define SERIAL_FRAME_ERROR 0x08 // Indicates that since getStatus() was last called there has been one or more framing error, this usually indicates a baud mismatch
|
||||||
|
#define SERIAL_AUTOBAUD_ENABLED 0x10 // Autobaud enabled.
|
||||||
|
#define SERIAL_AUTOBAUD_SYNC 0x20 // Indicates that a fresh baud rate has been set.
|
||||||
|
/* Some parts are afflicted by an errata that requires the receiver to be bounced to restore functioning after ICSIF is set and then cleared. the core does this automatically. */
|
||||||
|
#define SERIAL_AUTOBAUD_BADSYNC 0x30 // Indicates that an unsuccessful sync attempt was made. On parts known to be impacted by the errata, (AVR32DD20/14 - and maybe others)
|
||||||
|
// RXEN must be turned off and back on after clearing this. That is done when checkStatus() is called and finds ISFIF set.
|
||||||
|
#define SERIAL_OVERFLOW_RING 0x40 // Indicates that the *RING BUFFER* lost data due to overflow - you are not calling read() as fast as data is coming in.
|
||||||
|
#define SERIAL_OVERFLOW_HARDWARE 0x80 // Indicates that the *HARDWARE* buffer overflowed because interrupts were disabled for too long while receiving data.
|
||||||
|
|
||||||
|
|
||||||
|
/* CTRLA is interrupt flags, plus 3 options relevant to RS485
|
||||||
|
*
|
||||||
|
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | USARTn.CTRLA
|
||||||
|
* |---|---|---|---|---|---|---|---|
|
||||||
|
* |RXC|TXC|DRE|RXS| - |ABE| - | - | Interrupt Enable bits
|
||||||
|
* | - - - - |LBM| - | RS485 | RS485 stuff
|
||||||
|
*
|
||||||
|
* LBME - Loopback Mode Enable turns off RX and ties that channel to TX
|
||||||
|
* RS485 - RS485 mode tells it to drive XDIR pin HIGH while transmitting in
|
||||||
|
* order to control an external RS485 line driver. 1 bit of guard time on
|
||||||
|
* either side.
|
||||||
|
*
|
||||||
|
* RS485 field is strange - on 0/1-series, bit 1 was used was described as
|
||||||
|
* an transmit enable field for the UART, so an external device would use
|
||||||
|
* that to tell the device when it was allowed to drive the pin. But that
|
||||||
|
* has not been described in recent datasheets and the bit is marked as
|
||||||
|
* reserved, and only 1 RS485 mode was supported.
|
||||||
|
* Just as well - I've never tried it and am unsure of what it was supposed
|
||||||
|
* to do, but I suspect you that could debate how something like that "should"
|
||||||
|
* work in all the corner cases for weeks. It brings up all a great many
|
||||||
|
* issues, was probably a nightmare for designers, and considering the amount
|
||||||
|
* of errata on those parts, it probably didn't work very well.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* CTRLB configures a few major aspects of configuration:
|
||||||
|
*
|
||||||
|
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | USARTn.CTRLB
|
||||||
|
* |-------|---|---|---|-------|---|
|
||||||
|
* | TX RX | - |SFD|ODM|RX MODE|MPCM
|
||||||
|
* |ENABLE | - | Configuration | |
|
||||||
|
*
|
||||||
|
* TXEN - enables transmit functionality. When it transitions from enable to
|
||||||
|
* disabled, the pin is forcibly set to an input.
|
||||||
|
* RXEN - enables receive functionality
|
||||||
|
* SFDEN - enables Start of Frame detection to wake from sleep.
|
||||||
|
* ODME - enables Open Drain Mode. Tx pin must be INPUT instead of OUTPUT!
|
||||||
|
* MPCM - Multi Processor Communication Mode. A weird Atmel-only feature that
|
||||||
|
* turns a UART into a multidrop bus. I doubt it is in widespread use anywhere.
|
||||||
|
* Looks like the bastard son of UART and I2C that never got enough love or
|
||||||
|
* uptake to prosper. Likely dates to when Phillips was on the warpath
|
||||||
|
* regarding I2C intellectual property. (Maybe they were afraid that they would
|
||||||
|
* be forced to remove the TWI module?) Anyway, the core doesn't support it
|
||||||
|
* and it looks like a really ugly kludge.
|
||||||
|
* RXMODE - Controls the baud rate for both sending and receiving. 4 modes:
|
||||||
|
* Normal
|
||||||
|
* U2X - double-speed USART - much less important now with the fractional
|
||||||
|
* baud rate generator, but does the same thing. Serial.begin() chooses this
|
||||||
|
* if it needs to in order to generate the required baud rate only.
|
||||||
|
* GENAUTO - generic auto-baud mode
|
||||||
|
* LINAUTO - LIN constrained auto-baud mode
|
||||||
|
* Autobaud mode requires a "break" to be sent followed by a sync (0x55).
|
||||||
|
* LIN mode has more constraints in accordance with that stadatd, while
|
||||||
|
* generic mode has clearly just disabled the checks and added a WFB or
|
||||||
|
* "wait for break" option so you can use autobaud without having to
|
||||||
|
* guess what baud it's using.
|
||||||
|
*
|
||||||
|
* These parts have no trouble keeping a clock accurate enough for serial
|
||||||
|
* and since you need to have control over the software on the the device
|
||||||
|
* that it's talking so you can make it send the break/sync, and you are
|
||||||
|
* also programming this device, wherever it is viable, you know the baud
|
||||||
|
* rate that both ends are set to and don't need it. It's great if you need
|
||||||
|
* to meet the LIN specifications, of course, but that's not a typical or
|
||||||
|
* advisable Arduino use case...
|
||||||
|
* Based on persistent requests from users, who want it badly enough to try
|
||||||
|
* hacking something together to make use of it anyway, a future version
|
||||||
|
* will incorporate generic autobaud mode, even though it's pretty silly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Everything contigured by the SERIAL_xPs constants goes right into CTRLC:
|
||||||
|
*
|
||||||
|
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | USARTn.CTRLC
|
||||||
|
* |---|---|---|---|---|---|---|---|
|
||||||
|
* | MODE | PARITY| SB| CHAR SIZE |
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Plus there's there's the IRCOM - it is able to take an event input, and
|
||||||
|
* you can turn off the pulse coding that nobody's going to use in Arduino-land
|
||||||
|
* IRDA has been dead for years - we don't support using that, but we want event input.
|
||||||
|
*
|
||||||
|
* Additionally there are reasons to want to control to some bits from CTRLA + CTRLB too...
|
||||||
|
*
|
||||||
|
* It's also not clear yet how it actually behaves when disabled, namely:
|
||||||
|
* Can the event input be used in all modes, or must it be in IRCOM mode?
|
||||||
|
* Do we have to do something that applies the 115200 baud maximum to get it?
|
||||||
|
*
|
||||||
|
* We want a way to request that only TX, or only RX be enabled, but default should be both.
|
||||||
|
* And we want to control Open Drain, RS485 and LoopBack options independently of each other
|
||||||
|
*
|
||||||
|
* In summary:
|
||||||
|
*
|
||||||
|
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||||
|
* |---|---|---|---|---|---|---|---|
|
||||||
|
* | x x x | |LBM| |485| CTRLA
|
||||||
|
* |TXRX EN| |ODM| x __ | | CTRLB
|
||||||
|
* | |IREI EVCTRL
|
||||||
|
* Bits used actively for basic UART are marked with an x.
|
||||||
|
*
|
||||||
|
* That means 6 bits will be needed. Plus, we sometimes use use one of
|
||||||
|
* them internally, and the tinyAVR parts have that funky backwards
|
||||||
|
* RS485 option
|
||||||
|
* All the ones CTRLA default 0, and so we can AND with 0x09 (or 0x0B on tinyAVR) and OR with other CTRLA settings.
|
||||||
|
* And we want the two inverted CTRLB ones in their final locations so we can AND with 0xC0 and XOR the settings
|
||||||
|
* we're going to write to CTRLB. The other CTRLB one and the internal need to be done individually, so it doesn't matter
|
||||||
|
* that needs to set other options too.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* These are not usable on these parts. badCall() gets pulled in if you use them. */
|
||||||
|
|
||||||
|
#define SERIAL_PARITY_MARK (badArg("'Mark' as parity is not supported, use an extra stop bit"),0)
|
||||||
|
#define SERIAL_PARITY_SPACE (badArg("'Space' as parity is not supported on AVR"),0)
|
||||||
|
#define SERIAL_STOP_BIT_1_5 (badArg("1.5 stop bits is not a supported setting on AVR"),0)
|
||||||
|
#define SERIAL_5M1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_MARK | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6M1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_MARK | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7M1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_MARK | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8M1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 | SERIAL_PARITY_MARK | SERIAL_DATA_8)
|
||||||
|
#define SERIAL_5M2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_MARK | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6M2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_MARK | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7M2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_MARK | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8M2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 | SERIAL_PARITY_MARK | SERIAL_DATA_8)
|
||||||
|
#define SERIAL_5S1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 |SERIAL_PARITY_SPACE | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6S1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 |SERIAL_PARITY_SPACE | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7S1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 |SERIAL_PARITY_SPACE | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8S1 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_1 |SERIAL_PARITY_SPACE | SERIAL_DATA_8)
|
||||||
|
#define SERIAL_5S2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 |SERIAL_PARITY_SPACE | SERIAL_DATA_5)
|
||||||
|
#define SERIAL_6S2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 |SERIAL_PARITY_SPACE | SERIAL_DATA_6)
|
||||||
|
#define SERIAL_7S2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 |SERIAL_PARITY_SPACE | SERIAL_DATA_7)
|
||||||
|
#define SERIAL_8S2 (SERIAL_MODE_ASYNC | SERIAL_STOP_BIT_2 |SERIAL_PARITY_SPACE | SERIAL_DATA_8)
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* UART_private.h - Constructor for HardwareSerial
|
||||||
|
* This library is free software released under LGPL 2.1.
|
||||||
|
* See License.md for more information.
|
||||||
|
* This file is part of megaTinyCore
|
||||||
|
*
|
||||||
|
* Totally rewritten 2021-22 by Spence Konde and MX682X.
|
||||||
|
* The original version was written in 2006 by Nicholas
|
||||||
|
* Zambetti, and modified 11/23/2006 by David A. Mellis,
|
||||||
|
* 9/20/2010 by Mark Sproul, 8/24/2012 by Alarus, and
|
||||||
|
* other members of the Arduino community who did not sign.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wiring_private.h"
|
||||||
|
|
||||||
|
#if defined(USART0) || defined(USART1)
|
||||||
|
/* Significant changes in UART.cpp, UART.h, and UART_swap.h required to support more UARTs */
|
||||||
|
|
||||||
|
HardwareSerial::HardwareSerial(volatile USART_t *hwserial_module, uint8_t module_number, uint8_t mux_default) :
|
||||||
|
_hwserial_module(hwserial_module), _module_number(module_number), _pin_set(mux_default) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // whole file
|
|
@ -0,0 +1,535 @@
|
||||||
|
/* Completely rewritten 2021 Spence Konde;
|
||||||
|
* This is part of DxCore/megaTinyCore. Open source softeare (LGPL 2.1) see License.md for more info.
|
||||||
|
*
|
||||||
|
* attachInterrupt() that uses less flash and doesn't assimilate every interrupt vector...
|
||||||
|
*
|
||||||
|
* The ultimate goal was not achieved, but we can manually force the effect I wanted.
|
||||||
|
* Someone with deeper knowledge of how C/C++ decides that it will include the ISR from a file
|
||||||
|
* but it does give *a way* to write code that can use an attachInterrupt library and manually define pin interrupts.
|
||||||
|
* thisis important because attachInterrupt interrupts are miserably slow. Even in assembly, it's like 42 clocks to
|
||||||
|
* get to the loop, 7+ per pass through it, and a little over 40 more on the way out!
|
||||||
|
* Using lower numbered pins within a port minimizes how crappy this is. A port with 1 interrupt on bit 7 will take
|
||||||
|
* almost 42 clocks longer to be reached as it checks lower bits than one with it's one interrupt on bit 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "wiring_private.h"
|
||||||
|
|
||||||
|
#if !defined(CORE_ATTACH_OLD)
|
||||||
|
/* Well, I couldn't get it to autodetect awhere you're putting constant pins on interrupts, but there are tiny
|
||||||
|
* that you can call when using manual enabling;. Referencing them causes it to grab the associated vector, though it does have to be called to actually enable it - that's how we
|
||||||
|
* avoid wasting resourceses - each one not used also drops an array of 8 pointers to ISRs.
|
||||||
|
*
|
||||||
|
* attachPortAEnable() attachPortBEnable() attachPortCEnable() attachPortDEnable()
|
||||||
|
* attachPortEEnable() attachPortFEnable() attachPortGEnable()
|
||||||
|
*
|
||||||
|
* So you get the option wehether to enable it for each port. Each port also doesn't get it's own bloated ISR composed almost entirely of pushes and pops for calling a function
|
||||||
|
* it was done by doing rather evil things here but it does appear to work!
|
||||||
|
*/
|
||||||
|
/*Only create arrays for ports that exist) */
|
||||||
|
#if !defined(CORE_ATTACH_ALL)
|
||||||
|
extern voidFuncPtr * intFunc_A;
|
||||||
|
#ifdef PORTB_PINS
|
||||||
|
extern voidFuncPtr * intFunc_B;
|
||||||
|
#endif
|
||||||
|
#ifdef PORTC_PINS
|
||||||
|
extern voidFuncPtr * intFunc_C;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
voidFuncPtr intFunc_A[8];
|
||||||
|
#ifdef PORTB_PINS
|
||||||
|
voidFuncPtr intFunc_B[PORTB_PINS];
|
||||||
|
#endif
|
||||||
|
#ifdef PORTC_PINS
|
||||||
|
voidFuncPtr intFunc_C[PORTC_PINS];
|
||||||
|
#endif
|
||||||
|
volatile voidFuncPtr * intFunc[] = {
|
||||||
|
intFunc_A,
|
||||||
|
#ifdef PORTB_PINS
|
||||||
|
intFunc_B,
|
||||||
|
#endif
|
||||||
|
#ifdef PORTC_PINS
|
||||||
|
intFunc_C
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#if !defined(CORE_ATTACH_ALL)
|
||||||
|
#if defined(PORTC_PINS)
|
||||||
|
volatile voidFuncPtr * intFunc[] = {NULL, NULL, NULL};
|
||||||
|
#elif defined(PORTB_PINS)
|
||||||
|
volatile voidFuncPtr * intFunc[] = {NULL, NULL};
|
||||||
|
#else
|
||||||
|
volatile voidFuncPtr * intFunc[] = {NULL};
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
volatile uint8_t* portbase = (volatile uint8_t*)((uint16_t)0x400);
|
||||||
|
/* On modern AVRs, the PORT registers start at 0x400
|
||||||
|
* At 32 bytes per port, with plenty of room to spare, up to 8 ports will fit before we are forced into 0x500.
|
||||||
|
* And the rest of this implementation falls over if there's an 8th port because it relies on VPORTs
|
||||||
|
* of which there are only 7 ports worth of registers available. So this implementation is guaranteed not to work on a
|
||||||
|
* future part with 8 ports anyway. We will cross that bridge once Microchip has announced intent to build it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void attachInterrupt(uint8_t pin, void (*userFunc)(void), uint8_t mode) {
|
||||||
|
uint8_t bitpos = digitalPinToBitPosition(pin);
|
||||||
|
if (bitpos == NOT_A_PIN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t port = digitalPinToPort(pin);
|
||||||
|
switch (mode) {
|
||||||
|
case CHANGE:
|
||||||
|
mode = PORT_ISC_BOTHEDGES_gc;
|
||||||
|
break;
|
||||||
|
case FALLING:
|
||||||
|
mode = PORT_ISC_FALLING_gc;
|
||||||
|
break;
|
||||||
|
case RISING:
|
||||||
|
mode = PORT_ISC_RISING_gc;
|
||||||
|
break;
|
||||||
|
case LOW:
|
||||||
|
mode = PORT_ISC_LEVEL_gc;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (intFunc[port] != NULL && userFunc != NULL) {
|
||||||
|
// if it is null the port is not enabled for attachInterrupt, and obviously a null user function is invalid too.
|
||||||
|
intFunc[port][bitpos] = userFunc;
|
||||||
|
uint8_t portoffset = ((port << 5) & 0xE0) + 0x10 + bitpos;
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
// We now have the port, the mode, the bitpos and the pointer
|
||||||
|
uint8_t settings = *(portbase + portoffset) & 0xF8;
|
||||||
|
*(portbase + portoffset) = settings | mode;
|
||||||
|
SREG = oldSREG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if !defined(CORE_ATTACH_EARLYCLEAR) // late clear.
|
||||||
|
void __attribute__((naked)) __attribute__((used)) __attribute__((noreturn)) isrBody() {
|
||||||
|
asm volatile (
|
||||||
|
"AttachedISR:" "\n\t" // as the scene opens, we have r16 on the stack already, portnumber x 2 in the r16
|
||||||
|
"push r0" "\n\t" // so we start with a normal prologue
|
||||||
|
"in r0, 0x3f" "\n\t" // The SREG
|
||||||
|
"push r0" "\n\t" // on the stack
|
||||||
|
#if PROGMEM_SIZE > 0x10000
|
||||||
|
"in r0, 0x3b" "\n\t" // RAMPZ
|
||||||
|
"push r0" "\n\t" // on the stack.
|
||||||
|
#endif
|
||||||
|
"push r1" "\n\t" // We don't need r1 but the C code we call
|
||||||
|
"eor r1, r1" "\n\t" // is going to want this to be zero....
|
||||||
|
"push r15" "\n\t" // push r15 (we use it - it's call-saved)
|
||||||
|
"push r17" "\n\t" // and now we push all call used registers
|
||||||
|
"push r18" "\n\t" // except r16 which was pused over in WInterrupts_Px
|
||||||
|
"push r19" "\n\t"
|
||||||
|
"push r20" "\n\t"
|
||||||
|
"push r21" "\n\t"
|
||||||
|
"push r22" "\n\t"
|
||||||
|
"push r23" "\n\t"
|
||||||
|
"push r24" "\n\t"
|
||||||
|
"push r25" "\n\t"
|
||||||
|
"push r26" "\n\t"
|
||||||
|
"push r27" "\n\t"
|
||||||
|
"push r28" "\n\t" // Not call used, but we use it.
|
||||||
|
"push r29" "\n\t" // same thing.
|
||||||
|
"push r30" "\n\t"
|
||||||
|
"push r31" "\n\t"
|
||||||
|
::);
|
||||||
|
asm volatile ( // This gets us the address of intFunc in Y pointer reg.
|
||||||
|
"add r26, r16" "\n\t" // get the address of the functions for this port (r 16 is 2x the port number)
|
||||||
|
"adc r27, r1" "\n\t" // by adding that offset to the address we had the compiler generate the ldi's for
|
||||||
|
"ld r28, X+" "\n\t" // load the pointer to this port's function array...
|
||||||
|
"ld r29, X" "\n\t" // ... to the Y pointer reg.
|
||||||
|
"add r16, r16" "\n\t" // double r16, so it is 4x port number - that's the address of the start of the VPORT
|
||||||
|
"subi r16, 253" "\n\t" // Now this is the address of the VPORTx.INTFLAGS
|
||||||
|
"mov r26, r16" "\n\t" // r16 to x reg low byte ASM always lists the destination operand first.
|
||||||
|
"ldi r27, 0" "\n\t" // clear x high byte
|
||||||
|
"ld r15, X" "\n\t" // Load flags to r15"
|
||||||
|
"sbiw r26, 0" "\n\t" // subtract 0 from it - this serves as a single-word way to test if it's 0, because it will still set the Zero flag. It's no faster than cpi r26, 0, cpc r27, r1 but takes less flash.
|
||||||
|
"breq AIntEnd" "\n\t" // port not enabled, null pointer, just clear flags end hit the exit ramp.
|
||||||
|
"mov r17, r15" "\n\t" // copy the flags to r17
|
||||||
|
"AIntLoop:" "\n\t"
|
||||||
|
"lsr r17" "\n\t" // shift it right one place, now the LSB is in carry.
|
||||||
|
"brcs .+6" "\n\t" // means we have something to do this time.
|
||||||
|
"breq AIntEnd" "\n\t" // This means carry wasn't set and r17 is 0. - we're done.
|
||||||
|
"adiw r28, 2" "\n\t" // otherwise it's not a the int we care about, increment Y by 2, so it will point to the next element.
|
||||||
|
"rjmp AIntLoop" "\n\t" // restart the loop in that case.
|
||||||
|
"ld r30, Y+" "\n\t" // load the function pointer;
|
||||||
|
"ld r31, Y+" "\n\t" // load the function pointer;
|
||||||
|
"sbiw r30, 0" "\n\t" // zero-check it.
|
||||||
|
"breq AIntLoop" "\n\t" // restart loop if it is, don't call the null pointer
|
||||||
|
"icall" "\n\t" // call their function, which is allowed to shit on any upper registers other than 28, 29, 16, and 17.
|
||||||
|
"rjmp AIntLoop" "\n\t" // Restart loop after.
|
||||||
|
"AIntEnd:" "\n\t" // sooner or later r17 will be 0 and we'll branch here.
|
||||||
|
"mov r26, r16" "\n\t" // We previously stashed the VPORT address in r16, so copy that to low byte of X pointer <<BUG WAS HERE, r26 was copied to r16 instead of the other way around>>
|
||||||
|
"ldi r27, 0" "\n\t" // high byte is 0, cause we're targeting the VPORT, address < 0x20
|
||||||
|
"st X, r15" "\n\t" // store to clear the flags.... that we recorded at the start of the interrupt. (LATECLEAR)
|
||||||
|
"pop r31" "\n\t" // clean up a million registers
|
||||||
|
"pop r30" "\n\t"
|
||||||
|
"pop r29" "\n\t"
|
||||||
|
"pop r28" "\n\t"
|
||||||
|
"pop r27" "\n\t"
|
||||||
|
"pop r26" "\n\t"
|
||||||
|
"pop r25" "\n\t"
|
||||||
|
"pop r24" "\n\t"
|
||||||
|
"pop r23" "\n\t"
|
||||||
|
"pop r22" "\n\t"
|
||||||
|
"pop r21" "\n\t"
|
||||||
|
"pop r20" "\n\t"
|
||||||
|
"pop r19" "\n\t"
|
||||||
|
"pop r18" "\n\t"
|
||||||
|
"pop r17" "\n\t" // skip 16 again - it's way down at the end, because it was pushed earlier
|
||||||
|
"pop r15" "\n\t"
|
||||||
|
"pop r1" "\n\t"
|
||||||
|
#if PROGMEM_SIZE > 0x10000
|
||||||
|
"pop r0" "\n\t"
|
||||||
|
"out 0x3b, r0" "\n\t"
|
||||||
|
#endif
|
||||||
|
"pop r0" "\n\t"
|
||||||
|
"out 0x3f, r0" "\n\t" // between these is where there had been stuff added to the stack that we flushed.
|
||||||
|
"pop r0" "\n\t"
|
||||||
|
"pop r16" "\n\t" // this was the reg we pushed back in the port-specific file.
|
||||||
|
"reti" "\n" // now we should have the pointer to the return address fopr the ISR on top of the stack, so reti're
|
||||||
|
:: "x" ((uint16_t)(&intFunc))
|
||||||
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#else // EARLYCLEAR
|
||||||
|
void __attribute__((naked)) __attribute__((used)) __attribute__((noreturn)) isrBody() {
|
||||||
|
asm volatile (
|
||||||
|
"AttachedISR:" "\n\t" // as the scene opens, we have r16 on the stack already, portnumber x 2 in the r16
|
||||||
|
"push r0" "\n\t" // so we start with a normal prologue
|
||||||
|
"in r0, 0x3f" "\n\t" // The SREG
|
||||||
|
"push r0" "\n\t" // on the stack
|
||||||
|
"in r0, 0x3b" "\n\t" // RAMPZ
|
||||||
|
"push r0" "\n\t" // on the stack.
|
||||||
|
"push r1" "\n\t" // We don't need r1 but the C code we call
|
||||||
|
"eor r1, r1" "\n\t" // is going to want this to be zero....
|
||||||
|
// "push r15" "\n\t" // We don't use r15 with EARLYCLEAR.
|
||||||
|
// "push r17" "\n\t" // Eyyyyy lookit dat, we don't need r17 for EARLYCLEAR either. So that's two fewer call-saved registers that we need to save and restore.
|
||||||
|
"push r18" "\n\t" // and now we push all call used registers
|
||||||
|
"push r19" "\n\t" // except r16 which was pushed over in WInterrupts_Px
|
||||||
|
"push r20" "\n\t"
|
||||||
|
"push r21" "\n\t"
|
||||||
|
"push r22" "\n\t"
|
||||||
|
"push r23" "\n\t"
|
||||||
|
"push r24" "\n\t"
|
||||||
|
"push r25" "\n\t"
|
||||||
|
"push r26" "\n\t"
|
||||||
|
"push r27" "\n\t"
|
||||||
|
"push r28" "\n\t" // Not call used, but we use it.
|
||||||
|
"push r29" "\n\t" // And we need it's call-saved-ness ourselves to maintain state through the
|
||||||
|
"push r30" "\n\t"
|
||||||
|
"push r31" "\n\t"
|
||||||
|
::);
|
||||||
|
asm volatile ( // This gets us the address of intFunc in Y pointer reg.
|
||||||
|
"add r26, r16" "\n\t" // get the address of the functions for this port (r 16 is 2x the port number)
|
||||||
|
"adc r27, r1" "\n\t" // by adding that offset to the address we had the compiler generate the ldi's for;
|
||||||
|
"ld r28, X+" "\n\t" // That was the address of the start of array of pointers to arrays of pointers
|
||||||
|
"ld r29, X" "\n\t" // Now we loaded Y with the start of our port's array of function pointers
|
||||||
|
"add r16, r16" "\n\t" // double r16, so it is 4x port number - that's the address of the start of the VPORT
|
||||||
|
"subi r16, 253" "\n\t" // Add 3; now this is the address of the VPORTx.INTFLAGS
|
||||||
|
"mov r26, r16" "\n\t" // r16 holds the INTFLAGs address, copy it to X reg low byte.
|
||||||
|
"ldi r27, 0" "\n\t" // clear x high byte
|
||||||
|
"ld r16, X" "\n\t" // Load flags to r16"
|
||||||
|
"st X, r16" "\n\t" // EARLYCLEAR
|
||||||
|
"sbiw r26, 0" "\n\t" // this will set flag if it's zero.
|
||||||
|
"breq AIntEnd" "\n\t" // port not enabled, null pointer, just clear flags end hit the exit ramp.
|
||||||
|
"AIntLoop:" "\n\t"
|
||||||
|
"lsr r16" "\n\t" // shift it right one place, now the LSB is in carry.
|
||||||
|
"brcs .+6" "\n\t" // means we have something to do this time.
|
||||||
|
"breq AIntEnd" "\n\t" // This means carry wasn't set and r17 is 0. - we're done.
|
||||||
|
"adiw r28, 2" "\n\t" // otherwise it's not the int we care about, increment Y by 2, so it will point to the next element.
|
||||||
|
"rjmp AIntLoop" "\n\t" // restart the loop in that case.
|
||||||
|
"ld r30, Y+" "\n\t" // load the function pointer simultaneously advancing the Y pointer so next iteration it will
|
||||||
|
"ld r31, Y+" "\n\t" // be pointing to the next function pointer.
|
||||||
|
"sbiw r30, 0" "\n\t" // zero-check the pointer before we call it.
|
||||||
|
"breq AIntLoop" "\n\t" // restart loop if pointer is null. There is no interrupt handler yet interrupt is enabled and fired; Don't call the null pointer
|
||||||
|
"icall" "\n\t" // call their function, which is allowed to shit on any upper registers other than 28, 29, 16, and 17.
|
||||||
|
"rjmp AIntLoop" "\n\t" // Restart loop after.
|
||||||
|
"AIntEnd:" "\n\t" // sooner or later r17 will be 0 and we'll branch here.
|
||||||
|
// with EARLYCLEAR variant, we don't need to do anything other than cleaning up working registers - flags already cleared.
|
||||||
|
"pop r31" "\n\t" // clean up a million registers
|
||||||
|
"pop r30" "\n\t"
|
||||||
|
"pop r29" "\n\t"
|
||||||
|
"pop r28" "\n\t"
|
||||||
|
"pop r27" "\n\t"
|
||||||
|
"pop r26" "\n\t"
|
||||||
|
"pop r25" "\n\t"
|
||||||
|
"pop r24" "\n\t"
|
||||||
|
"pop r23" "\n\t"
|
||||||
|
"pop r22" "\n\t"
|
||||||
|
"pop r21" "\n\t"
|
||||||
|
"pop r20" "\n\t"
|
||||||
|
"pop r19" "\n\t" // skip 16 again - it's way down at the end, because it was pushed earlier
|
||||||
|
"pop r18" "\n\t"
|
||||||
|
//"pop r17" "\n\t" // Early clear doesn't need the extra registers.
|
||||||
|
//"pop r15" "\n\t" // Early clear doesn't need the extra registers.
|
||||||
|
"pop r1" "\n\t"
|
||||||
|
"pop r0" "\n\t"
|
||||||
|
"out 0x3b, r0" "\n\t"
|
||||||
|
"pop r0" "\n\t"
|
||||||
|
"out 0x3f, r0" "\n\t" // between these is where there had been stuff added to the stack that we flushed.
|
||||||
|
"pop r0" "\n\t"
|
||||||
|
"pop r16" "\n\t" // this was the reg we pushed back in the port-specific file.
|
||||||
|
"reti" "\n" // now we should have the pointer to the return address fopr the ISR on top of the stack, so reti're
|
||||||
|
:: "x" ((uint16_t)(&intFunc))
|
||||||
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void detachInterrupt(uint8_t pin) {
|
||||||
|
/* Get bit position and check pin validity */
|
||||||
|
uint8_t bitpos = digitalPinToBitPosition(pin);
|
||||||
|
if (bitpos == NOT_A_PIN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t port = digitalPinToPort(pin);
|
||||||
|
uint8_t p = (port << 5) + bitpos;
|
||||||
|
*(((volatile uint8_t*) &PORTA_PIN0CTRL) + p) &= 0xF8; // int off....
|
||||||
|
*((volatile uint8_t*) ((uint16_t)((port << 4) + 3))) = (1 << bitpos);// flag clear
|
||||||
|
intFunc[port][bitpos] = 0; // clear pointer
|
||||||
|
}
|
||||||
|
/* If not enabling attach on all ports always, instead the identical ISR definitions are in the WInterruptsA/B/C/D/E/F/G.c files.
|
||||||
|
* Okay, so what the f-- is going on here?
|
||||||
|
* To avoid each interrupt vector having it's own lengthy prologue and epilog separaely, which is needed in order for a function call to be made in an ISR
|
||||||
|
* All we do is push an upper register onto the stack so can load a value twice the PORT number there, and jump to actual function that does the work here.
|
||||||
|
*
|
||||||
|
* The isrBody() has two consecutive blocks of inline assembly, First, do what is basically a standard prologue, for something that calls a function (thus there is only one prologue, instead of one per port).
|
||||||
|
* We finish the prologue but we need to push the Y pointer and r15 (call saved) for this routine. Then we slip out out of that assembly block just to grab the pointer to IntFunc array, which we need in a pointer reg;
|
||||||
|
* The second block of inline ASM specifies that the pointer to the array of pointers to arrays of pointers to interrupt functions be passed in the X pointer register
|
||||||
|
*
|
||||||
|
* Assembly is split up only so we can grab that address through that in a constraint.
|
||||||
|
* We couldn't have done that any sooner, we had nowhere to put it. To that we add the pre-doubled port number - pointers are 2 bytes so we need that
|
||||||
|
* doubling.
|
||||||
|
*
|
||||||
|
* We can then load the pointer to this port's int functions to the call-saved Y register.
|
||||||
|
* Sutract 0 from that pointer to check that it's not 0. A null pointer is of no use. Assuming that there's a pointer, we continue.
|
||||||
|
* Next we need the INTFLAGs. We will get them from the VPORT, not the PORT. This is much easier. We double r16 for address of VPORT and add 3 with subi to
|
||||||
|
* get INTFLAGs, copy to r26, and load the 0 high byte to r27, read the flags into r15.
|
||||||
|
*
|
||||||
|
* Copy flags to r17 and then start the loop. We will check the intflags one at a time. We rightshift once, and check if carry is set, indicating we pushed a 1 out, if so we jump over
|
||||||
|
* three instructions handling the case if we it was a 0: We check then check first for the carry bit. If not, then we check if the zero flag is set, If THAT is then what's left of flags
|
||||||
|
* is and we've handled them all, and we jump to end. and if it's not, we increment the Y pointer by 2 and return to start of the loop
|
||||||
|
*
|
||||||
|
* For each of the ones that we do have a flag for, we load that pointer into Z with postincrement, subtract 0 from it and look at zero flag to make sure it's not null.
|
||||||
|
* assuming it's not, we fire icall to call the user function. Either way we then repeat the loop until out of flags.
|
||||||
|
* which at latest will happen when we're also at end of the ports intfunc array....
|
||||||
|
* Then, with the initial flags still in 15 and the the VPORT address in r16 copy that once more to a pointer register, 0 the high byte, and store the flags value we read to clear it.
|
||||||
|
* then it's just a matter of making sure we pop everything we pushed onto the stack in the reverse order, including r16 followed by the reti to exit the interrupt..
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(CORE_ATTACH_ALL)
|
||||||
|
ISR(PORTA_PORT_vect, ISR_NAKED) {
|
||||||
|
asm volatile(
|
||||||
|
"push r16" "\n\t"
|
||||||
|
"ldi r16, 0" "\n\t"
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp AttachedISR" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp AttachedISR" "\n\t"
|
||||||
|
#endif
|
||||||
|
::);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#ifdef PORTB_PINS
|
||||||
|
ISR(PORTB_PORT_vect, ISR_NAKED) {
|
||||||
|
asm volatile(
|
||||||
|
"push r16" "\n\t"
|
||||||
|
"ldi r16, 2" "\n\t"
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp AttachedISR" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp AttachedISR" "\n\t"
|
||||||
|
#endif
|
||||||
|
::);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef PORTC_PINS
|
||||||
|
ISR(PORTC_PORT_vect, ISR_NAKED) {
|
||||||
|
asm volatile(
|
||||||
|
"push r16" "\n\t"
|
||||||
|
"ldi r16, 4" "\n\t"
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp AttachedISR" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp AttachedISR" "\n\t"
|
||||||
|
#endif
|
||||||
|
::);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#else /* This is the old implementation, and it's copyright boilerplate. */
|
||||||
|
|
||||||
|
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ //<---- FFS! That was in the original!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of the Wiring project - http://wiring.uniandes.edu.co
|
||||||
|
|
||||||
|
Copyright (c) 2004-05 Hernando Barragan
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
Modified 24 November 2006 by David A. Mellis
|
||||||
|
Modified 1 August 2010 by Mark Sproul
|
||||||
|
(Spence: and presumably someone who didn't sign to adapt it to modernAVRs,
|
||||||
|
since nothing that this implementation would work with had been released until
|
||||||
|
2016)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "wiring_private.h"
|
||||||
|
|
||||||
|
static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS];
|
||||||
|
|
||||||
|
void attachInterrupt(uint8_t pin, void (*userFunc)(void), uint8_t mode) {
|
||||||
|
|
||||||
|
/* Get bit position and check pin validity */
|
||||||
|
uint8_t bit_pos = digitalPinToBitPosition(pin);
|
||||||
|
if (bit_pos == NOT_A_PIN) return;
|
||||||
|
|
||||||
|
/* Get interrupt number from pin */
|
||||||
|
uint8_t interruptNum = (digitalPinToPort(pin) * 8) + bit_pos;
|
||||||
|
|
||||||
|
/* Check interrupt number and apply function pointer to correct array index */
|
||||||
|
if (interruptNum < EXTERNAL_NUM_INTERRUPTS) {
|
||||||
|
intFunc[interruptNum] = userFunc;
|
||||||
|
|
||||||
|
// Configure the interrupt mode (trigger on low input, any change, rising
|
||||||
|
// edge, or falling edge)
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case CHANGE:
|
||||||
|
mode = PORT_ISC_BOTHEDGES_gc;
|
||||||
|
break;
|
||||||
|
case FALLING:
|
||||||
|
mode = PORT_ISC_FALLING_gc;
|
||||||
|
break;
|
||||||
|
case RISING:
|
||||||
|
mode = PORT_ISC_RISING_gc;
|
||||||
|
break;
|
||||||
|
case LOW:
|
||||||
|
mode = PORT_ISC_LEVEL_gc;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the interrupt.
|
||||||
|
|
||||||
|
/* Get pointer to correct pin control register */
|
||||||
|
PORT_t *port = digitalPinToPortStruct(pin);
|
||||||
|
volatile uint8_t* pin_ctrl_reg = getPINnCTRLregister(port, bit_pos);
|
||||||
|
|
||||||
|
/* Clear any previous setting */
|
||||||
|
*pin_ctrl_reg &= ~(PORT_ISC_gm);
|
||||||
|
|
||||||
|
/* Apply ISC setting */
|
||||||
|
*pin_ctrl_reg |= mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void detachInterrupt(uint8_t pin) {
|
||||||
|
/* Get bit position and check pin validity */
|
||||||
|
uint8_t bit_pos = digitalPinToBitPosition(pin);
|
||||||
|
if (bit_pos == NOT_A_PIN) return;
|
||||||
|
|
||||||
|
/* Get interrupt number from pin */
|
||||||
|
uint8_t interruptNum = (digitalPinToPort(pin) * 8) + bit_pos;
|
||||||
|
|
||||||
|
if (interruptNum < EXTERNAL_NUM_INTERRUPTS) {
|
||||||
|
// Disable the interrupt.
|
||||||
|
|
||||||
|
/* Get pointer to correct pin control register */
|
||||||
|
PORT_t *port = digitalPinToPortStruct(pin);
|
||||||
|
volatile uint8_t* pin_ctrl_reg = getPINnCTRLregister(port, bit_pos);
|
||||||
|
|
||||||
|
/* Clear ISC setting */
|
||||||
|
*pin_ctrl_reg &= ~(PORT_ISC_gm);
|
||||||
|
|
||||||
|
intFunc[interruptNum] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void port_interrupt_handler(uint8_t port) {
|
||||||
|
|
||||||
|
PORT_t *portStruct = portToPortStruct(port);
|
||||||
|
/* Copy flags */
|
||||||
|
uint8_t int_flags = portStruct->INTFLAGS;
|
||||||
|
|
||||||
|
uint8_t bit_pos = PIN0_bp, bit_mask = PIN0_bm;
|
||||||
|
|
||||||
|
/* Iterate through flags */
|
||||||
|
while(bit_pos <= PIN7_bp) {
|
||||||
|
|
||||||
|
/* Check if flag raised */
|
||||||
|
if (int_flags & bit_mask) {
|
||||||
|
|
||||||
|
/* Get interrupt */
|
||||||
|
uint8_t interrupt_num = port*8 + bit_pos;
|
||||||
|
|
||||||
|
/* Check if function defined */
|
||||||
|
if (intFunc[interrupt_num] != 0) {
|
||||||
|
|
||||||
|
/* Call function */
|
||||||
|
intFunc[interrupt_num]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bit_pos++;
|
||||||
|
bit_mask = (bit_mask << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear flags that have been handled */
|
||||||
|
portStruct->INTFLAGS = int_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IMPLEMENT_ISR(vect, port) \
|
||||||
|
ISR(vect) { \
|
||||||
|
port_interrupt_handler(port);\
|
||||||
|
} \
|
||||||
|
|
||||||
|
|
||||||
|
IMPLEMENT_ISR(PORTA_PORT_vect, PA)
|
||||||
|
#if defined(PORTB_PORT_vect)
|
||||||
|
IMPLEMENT_ISR(PORTB_PORT_vect, PB)
|
||||||
|
#endif
|
||||||
|
#if defined(PORTC_PORT_vect)
|
||||||
|
IMPLEMENT_ISR(PORTC_PORT_vect, PC)
|
||||||
|
#endif
|
||||||
|
#if defined(PORTD_PORT_vect)
|
||||||
|
IMPLEMENT_ISR(PORTD_PORT_vect, PD)
|
||||||
|
#endif
|
||||||
|
#if defined(PORTE_PORT_vect)
|
||||||
|
IMPLEMENT_ISR(PORTE_PORT_vect, PE)
|
||||||
|
#endif
|
||||||
|
#if defined(PORTF_PORT_vect)
|
||||||
|
IMPLEMENT_ISR(PORTF_PORT_vect, PF)
|
||||||
|
#endif
|
||||||
|
// Nope, there was never attachInterrupt for PORTG and nobody complained for 2 years. Not going to change that -Speence 3/5/22
|
||||||
|
#endif
|
|
@ -0,0 +1,34 @@
|
||||||
|
#if !defined(CORE_ATTACH_OLD) && !defined(CORE_ATTACH_ALL)
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "wiring_private.h"
|
||||||
|
|
||||||
|
#ifdef PORTA
|
||||||
|
extern voidFuncPtr * intFunc[];
|
||||||
|
voidFuncPtr intFunc_A[8];
|
||||||
|
void attachPortAEnable() {
|
||||||
|
intFunc[0] = intFunc_A;
|
||||||
|
}
|
||||||
|
/* It is the act of referencing attachPortAEnable() in this file that the compiler to also include the ISR.
|
||||||
|
* (ISRs can't be assigned at runtime - the only way things like attachInterruprt look like they can is
|
||||||
|
* by having the ISR call a function pointer (ie, icall). After saving every call used plus SREG and RAMPZ.
|
||||||
|
* Beyond being slow, these lengthy prologues and epilogs slow take up flash. This can be like 80+ bytes
|
||||||
|
* per vector... That's ugly, particularly on small flash parts. See the discussion in WInterrupts.c about
|
||||||
|
* what this actually does.
|
||||||
|
*/
|
||||||
|
ISR(PORTA_PORT_vect, ISR_NAKED) {
|
||||||
|
asm volatile(
|
||||||
|
"push r16" "\n\t"
|
||||||
|
"ldi r16, 0" "\n\t"
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp AttachedISR" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp AttachedISR" "\n\t"
|
||||||
|
#endif
|
||||||
|
::);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
#if !defined(CORE_ATTACH_OLD) && !defined(CORE_ATTACH_ALL)
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "wiring_private.h"
|
||||||
|
#if defined(PORTB_PINS)
|
||||||
|
extern voidFuncPtr * intFunc[];
|
||||||
|
voidFuncPtr intFunc_B[PORTB_PINS];
|
||||||
|
void attachPortBEnable() {
|
||||||
|
intFunc[1] = intFunc_B;
|
||||||
|
}
|
||||||
|
ISR(PORTB_PORT_vect, ISR_NAKED) {
|
||||||
|
asm volatile(
|
||||||
|
"push r16" "\n\t"
|
||||||
|
"ldi r16, 2" "\n\t"
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp AttachedISR" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp AttachedISR" "\n\t"
|
||||||
|
#endif
|
||||||
|
::);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
#if !defined(CORE_ATTACH_OLD) && !defined(CORE_ATTACH_ALL)
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "wiring_private.h"
|
||||||
|
#if defined(PORTC_PINS)
|
||||||
|
extern voidFuncPtr * intFunc[];
|
||||||
|
voidFuncPtr intFunc_C[PORTC_PINS];
|
||||||
|
void attachPortCEnable() {
|
||||||
|
intFunc[2] = intFunc_C;
|
||||||
|
}
|
||||||
|
ISR(PORTC_PORT_vect, ISR_NAKED) {
|
||||||
|
asm volatile(
|
||||||
|
"push r16" "\n\t"
|
||||||
|
"ldi r16, 4" "\n\t"
|
||||||
|
#if PROGMEM_SIZE > 8192
|
||||||
|
"jmp AttachedISR" "\n\t"
|
||||||
|
#else
|
||||||
|
"rjmp AttachedISR" "\n\t"
|
||||||
|
#endif
|
||||||
|
::);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of the Wiring project - http://wiring.org.co
|
||||||
|
Copyright (c) 2004-06 Hernando Barragan
|
||||||
|
Modified 13 August 2006, David A. Mellis for Arduino - http://www.arduino.cc/
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "stdlib.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
void randomSeed(unsigned long seed) {
|
||||||
|
if (seed != 0) {
|
||||||
|
srandom(seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long random(long howbig) {
|
||||||
|
if (howbig == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return random() % howbig;
|
||||||
|
}
|
||||||
|
|
||||||
|
long random(long howsmall, long howbig) {
|
||||||
|
if (howsmall >= howbig) {
|
||||||
|
return howsmall;
|
||||||
|
}
|
||||||
|
long diff = howbig - howsmall;
|
||||||
|
return random(diff) + howsmall;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 Arduino. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
extern "C" void __cxa_pure_virtual(void) __attribute__((__noreturn__));
|
||||||
|
extern "C" void __cxa_deleted_virtual(void) __attribute__((__noreturn__));
|
||||||
|
|
||||||
|
void __cxa_pure_virtual(void) {
|
||||||
|
// We might want to write some diagnostics to uart in this case
|
||||||
|
// std::terminate();
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __cxa_deleted_virtual(void) {
|
||||||
|
// We might want to write some diagnostics to uart in this case
|
||||||
|
// std::terminate();
|
||||||
|
abort();
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Arduino API main include
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARDUINO_API_H
|
||||||
|
#define ARDUINO_API_H
|
||||||
|
|
||||||
|
// version 1.0.0
|
||||||
|
#define ARDUINO_API_VERSION 10000
|
||||||
|
|
||||||
|
#include "Binary.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include "Client.h"
|
||||||
|
#include "HardwareI2C.h"
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
#include "IPAddress.h"
|
||||||
|
#include "Print.h"
|
||||||
|
#include "Printable.h"
|
||||||
|
#include "PluggableUSB.h"
|
||||||
|
#include "Server.h"
|
||||||
|
#include "String.h"
|
||||||
|
#include "Stream.h"
|
||||||
|
#include "Udp.h"
|
||||||
|
#include "USBAPI.h"
|
||||||
|
#include "WCharacter.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Standard C library includes */
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
// Misc Arduino core functions
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,534 @@
|
||||||
|
/*
|
||||||
|
binary.h - Definitions for binary constants
|
||||||
|
Copyright (c) 2006 David A. Mellis. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Binary_h
|
||||||
|
#define Binary_h
|
||||||
|
|
||||||
|
#define B0 0
|
||||||
|
#define B00 0
|
||||||
|
#define B000 0
|
||||||
|
#define B0000 0
|
||||||
|
#define B00000 0
|
||||||
|
#define B000000 0
|
||||||
|
#define B0000000 0
|
||||||
|
#define B00000000 0
|
||||||
|
#define B1 1
|
||||||
|
#define B01 1
|
||||||
|
#define B001 1
|
||||||
|
#define B0001 1
|
||||||
|
#define B00001 1
|
||||||
|
#define B000001 1
|
||||||
|
#define B0000001 1
|
||||||
|
#define B00000001 1
|
||||||
|
#define B10 2
|
||||||
|
#define B010 2
|
||||||
|
#define B0010 2
|
||||||
|
#define B00010 2
|
||||||
|
#define B000010 2
|
||||||
|
#define B0000010 2
|
||||||
|
#define B00000010 2
|
||||||
|
#define B11 3
|
||||||
|
#define B011 3
|
||||||
|
#define B0011 3
|
||||||
|
#define B00011 3
|
||||||
|
#define B000011 3
|
||||||
|
#define B0000011 3
|
||||||
|
#define B00000011 3
|
||||||
|
#define B100 4
|
||||||
|
#define B0100 4
|
||||||
|
#define B00100 4
|
||||||
|
#define B000100 4
|
||||||
|
#define B0000100 4
|
||||||
|
#define B00000100 4
|
||||||
|
#define B101 5
|
||||||
|
#define B0101 5
|
||||||
|
#define B00101 5
|
||||||
|
#define B000101 5
|
||||||
|
#define B0000101 5
|
||||||
|
#define B00000101 5
|
||||||
|
#define B110 6
|
||||||
|
#define B0110 6
|
||||||
|
#define B00110 6
|
||||||
|
#define B000110 6
|
||||||
|
#define B0000110 6
|
||||||
|
#define B00000110 6
|
||||||
|
#define B111 7
|
||||||
|
#define B0111 7
|
||||||
|
#define B00111 7
|
||||||
|
#define B000111 7
|
||||||
|
#define B0000111 7
|
||||||
|
#define B00000111 7
|
||||||
|
#define B1000 8
|
||||||
|
#define B01000 8
|
||||||
|
#define B001000 8
|
||||||
|
#define B0001000 8
|
||||||
|
#define B00001000 8
|
||||||
|
#define B1001 9
|
||||||
|
#define B01001 9
|
||||||
|
#define B001001 9
|
||||||
|
#define B0001001 9
|
||||||
|
#define B00001001 9
|
||||||
|
#define B1010 10
|
||||||
|
#define B01010 10
|
||||||
|
#define B001010 10
|
||||||
|
#define B0001010 10
|
||||||
|
#define B00001010 10
|
||||||
|
#define B1011 11
|
||||||
|
#define B01011 11
|
||||||
|
#define B001011 11
|
||||||
|
#define B0001011 11
|
||||||
|
#define B00001011 11
|
||||||
|
#define B1100 12
|
||||||
|
#define B01100 12
|
||||||
|
#define B001100 12
|
||||||
|
#define B0001100 12
|
||||||
|
#define B00001100 12
|
||||||
|
#define B1101 13
|
||||||
|
#define B01101 13
|
||||||
|
#define B001101 13
|
||||||
|
#define B0001101 13
|
||||||
|
#define B00001101 13
|
||||||
|
#define B1110 14
|
||||||
|
#define B01110 14
|
||||||
|
#define B001110 14
|
||||||
|
#define B0001110 14
|
||||||
|
#define B00001110 14
|
||||||
|
#define B1111 15
|
||||||
|
#define B01111 15
|
||||||
|
#define B001111 15
|
||||||
|
#define B0001111 15
|
||||||
|
#define B00001111 15
|
||||||
|
#define B10000 16
|
||||||
|
#define B010000 16
|
||||||
|
#define B0010000 16
|
||||||
|
#define B00010000 16
|
||||||
|
#define B10001 17
|
||||||
|
#define B010001 17
|
||||||
|
#define B0010001 17
|
||||||
|
#define B00010001 17
|
||||||
|
#define B10010 18
|
||||||
|
#define B010010 18
|
||||||
|
#define B0010010 18
|
||||||
|
#define B00010010 18
|
||||||
|
#define B10011 19
|
||||||
|
#define B010011 19
|
||||||
|
#define B0010011 19
|
||||||
|
#define B00010011 19
|
||||||
|
#define B10100 20
|
||||||
|
#define B010100 20
|
||||||
|
#define B0010100 20
|
||||||
|
#define B00010100 20
|
||||||
|
#define B10101 21
|
||||||
|
#define B010101 21
|
||||||
|
#define B0010101 21
|
||||||
|
#define B00010101 21
|
||||||
|
#define B10110 22
|
||||||
|
#define B010110 22
|
||||||
|
#define B0010110 22
|
||||||
|
#define B00010110 22
|
||||||
|
#define B10111 23
|
||||||
|
#define B010111 23
|
||||||
|
#define B0010111 23
|
||||||
|
#define B00010111 23
|
||||||
|
#define B11000 24
|
||||||
|
#define B011000 24
|
||||||
|
#define B0011000 24
|
||||||
|
#define B00011000 24
|
||||||
|
#define B11001 25
|
||||||
|
#define B011001 25
|
||||||
|
#define B0011001 25
|
||||||
|
#define B00011001 25
|
||||||
|
#define B11010 26
|
||||||
|
#define B011010 26
|
||||||
|
#define B0011010 26
|
||||||
|
#define B00011010 26
|
||||||
|
#define B11011 27
|
||||||
|
#define B011011 27
|
||||||
|
#define B0011011 27
|
||||||
|
#define B00011011 27
|
||||||
|
#define B11100 28
|
||||||
|
#define B011100 28
|
||||||
|
#define B0011100 28
|
||||||
|
#define B00011100 28
|
||||||
|
#define B11101 29
|
||||||
|
#define B011101 29
|
||||||
|
#define B0011101 29
|
||||||
|
#define B00011101 29
|
||||||
|
#define B11110 30
|
||||||
|
#define B011110 30
|
||||||
|
#define B0011110 30
|
||||||
|
#define B00011110 30
|
||||||
|
#define B11111 31
|
||||||
|
#define B011111 31
|
||||||
|
#define B0011111 31
|
||||||
|
#define B00011111 31
|
||||||
|
#define B100000 32
|
||||||
|
#define B0100000 32
|
||||||
|
#define B00100000 32
|
||||||
|
#define B100001 33
|
||||||
|
#define B0100001 33
|
||||||
|
#define B00100001 33
|
||||||
|
#define B100010 34
|
||||||
|
#define B0100010 34
|
||||||
|
#define B00100010 34
|
||||||
|
#define B100011 35
|
||||||
|
#define B0100011 35
|
||||||
|
#define B00100011 35
|
||||||
|
#define B100100 36
|
||||||
|
#define B0100100 36
|
||||||
|
#define B00100100 36
|
||||||
|
#define B100101 37
|
||||||
|
#define B0100101 37
|
||||||
|
#define B00100101 37
|
||||||
|
#define B100110 38
|
||||||
|
#define B0100110 38
|
||||||
|
#define B00100110 38
|
||||||
|
#define B100111 39
|
||||||
|
#define B0100111 39
|
||||||
|
#define B00100111 39
|
||||||
|
#define B101000 40
|
||||||
|
#define B0101000 40
|
||||||
|
#define B00101000 40
|
||||||
|
#define B101001 41
|
||||||
|
#define B0101001 41
|
||||||
|
#define B00101001 41
|
||||||
|
#define B101010 42
|
||||||
|
#define B0101010 42
|
||||||
|
#define B00101010 42
|
||||||
|
#define B101011 43
|
||||||
|
#define B0101011 43
|
||||||
|
#define B00101011 43
|
||||||
|
#define B101100 44
|
||||||
|
#define B0101100 44
|
||||||
|
#define B00101100 44
|
||||||
|
#define B101101 45
|
||||||
|
#define B0101101 45
|
||||||
|
#define B00101101 45
|
||||||
|
#define B101110 46
|
||||||
|
#define B0101110 46
|
||||||
|
#define B00101110 46
|
||||||
|
#define B101111 47
|
||||||
|
#define B0101111 47
|
||||||
|
#define B00101111 47
|
||||||
|
#define B110000 48
|
||||||
|
#define B0110000 48
|
||||||
|
#define B00110000 48
|
||||||
|
#define B110001 49
|
||||||
|
#define B0110001 49
|
||||||
|
#define B00110001 49
|
||||||
|
#define B110010 50
|
||||||
|
#define B0110010 50
|
||||||
|
#define B00110010 50
|
||||||
|
#define B110011 51
|
||||||
|
#define B0110011 51
|
||||||
|
#define B00110011 51
|
||||||
|
#define B110100 52
|
||||||
|
#define B0110100 52
|
||||||
|
#define B00110100 52
|
||||||
|
#define B110101 53
|
||||||
|
#define B0110101 53
|
||||||
|
#define B00110101 53
|
||||||
|
#define B110110 54
|
||||||
|
#define B0110110 54
|
||||||
|
#define B00110110 54
|
||||||
|
#define B110111 55
|
||||||
|
#define B0110111 55
|
||||||
|
#define B00110111 55
|
||||||
|
#define B111000 56
|
||||||
|
#define B0111000 56
|
||||||
|
#define B00111000 56
|
||||||
|
#define B111001 57
|
||||||
|
#define B0111001 57
|
||||||
|
#define B00111001 57
|
||||||
|
#define B111010 58
|
||||||
|
#define B0111010 58
|
||||||
|
#define B00111010 58
|
||||||
|
#define B111011 59
|
||||||
|
#define B0111011 59
|
||||||
|
#define B00111011 59
|
||||||
|
#define B111100 60
|
||||||
|
#define B0111100 60
|
||||||
|
#define B00111100 60
|
||||||
|
#define B111101 61
|
||||||
|
#define B0111101 61
|
||||||
|
#define B00111101 61
|
||||||
|
#define B111110 62
|
||||||
|
#define B0111110 62
|
||||||
|
#define B00111110 62
|
||||||
|
#define B111111 63
|
||||||
|
#define B0111111 63
|
||||||
|
#define B00111111 63
|
||||||
|
#define B1000000 64
|
||||||
|
#define B01000000 64
|
||||||
|
#define B1000001 65
|
||||||
|
#define B01000001 65
|
||||||
|
#define B1000010 66
|
||||||
|
#define B01000010 66
|
||||||
|
#define B1000011 67
|
||||||
|
#define B01000011 67
|
||||||
|
#define B1000100 68
|
||||||
|
#define B01000100 68
|
||||||
|
#define B1000101 69
|
||||||
|
#define B01000101 69
|
||||||
|
#define B1000110 70
|
||||||
|
#define B01000110 70
|
||||||
|
#define B1000111 71
|
||||||
|
#define B01000111 71
|
||||||
|
#define B1001000 72
|
||||||
|
#define B01001000 72
|
||||||
|
#define B1001001 73
|
||||||
|
#define B01001001 73
|
||||||
|
#define B1001010 74
|
||||||
|
#define B01001010 74
|
||||||
|
#define B1001011 75
|
||||||
|
#define B01001011 75
|
||||||
|
#define B1001100 76
|
||||||
|
#define B01001100 76
|
||||||
|
#define B1001101 77
|
||||||
|
#define B01001101 77
|
||||||
|
#define B1001110 78
|
||||||
|
#define B01001110 78
|
||||||
|
#define B1001111 79
|
||||||
|
#define B01001111 79
|
||||||
|
#define B1010000 80
|
||||||
|
#define B01010000 80
|
||||||
|
#define B1010001 81
|
||||||
|
#define B01010001 81
|
||||||
|
#define B1010010 82
|
||||||
|
#define B01010010 82
|
||||||
|
#define B1010011 83
|
||||||
|
#define B01010011 83
|
||||||
|
#define B1010100 84
|
||||||
|
#define B01010100 84
|
||||||
|
#define B1010101 85
|
||||||
|
#define B01010101 85
|
||||||
|
#define B1010110 86
|
||||||
|
#define B01010110 86
|
||||||
|
#define B1010111 87
|
||||||
|
#define B01010111 87
|
||||||
|
#define B1011000 88
|
||||||
|
#define B01011000 88
|
||||||
|
#define B1011001 89
|
||||||
|
#define B01011001 89
|
||||||
|
#define B1011010 90
|
||||||
|
#define B01011010 90
|
||||||
|
#define B1011011 91
|
||||||
|
#define B01011011 91
|
||||||
|
#define B1011100 92
|
||||||
|
#define B01011100 92
|
||||||
|
#define B1011101 93
|
||||||
|
#define B01011101 93
|
||||||
|
#define B1011110 94
|
||||||
|
#define B01011110 94
|
||||||
|
#define B1011111 95
|
||||||
|
#define B01011111 95
|
||||||
|
#define B1100000 96
|
||||||
|
#define B01100000 96
|
||||||
|
#define B1100001 97
|
||||||
|
#define B01100001 97
|
||||||
|
#define B1100010 98
|
||||||
|
#define B01100010 98
|
||||||
|
#define B1100011 99
|
||||||
|
#define B01100011 99
|
||||||
|
#define B1100100 100
|
||||||
|
#define B01100100 100
|
||||||
|
#define B1100101 101
|
||||||
|
#define B01100101 101
|
||||||
|
#define B1100110 102
|
||||||
|
#define B01100110 102
|
||||||
|
#define B1100111 103
|
||||||
|
#define B01100111 103
|
||||||
|
#define B1101000 104
|
||||||
|
#define B01101000 104
|
||||||
|
#define B1101001 105
|
||||||
|
#define B01101001 105
|
||||||
|
#define B1101010 106
|
||||||
|
#define B01101010 106
|
||||||
|
#define B1101011 107
|
||||||
|
#define B01101011 107
|
||||||
|
#define B1101100 108
|
||||||
|
#define B01101100 108
|
||||||
|
#define B1101101 109
|
||||||
|
#define B01101101 109
|
||||||
|
#define B1101110 110
|
||||||
|
#define B01101110 110
|
||||||
|
#define B1101111 111
|
||||||
|
#define B01101111 111
|
||||||
|
#define B1110000 112
|
||||||
|
#define B01110000 112
|
||||||
|
#define B1110001 113
|
||||||
|
#define B01110001 113
|
||||||
|
#define B1110010 114
|
||||||
|
#define B01110010 114
|
||||||
|
#define B1110011 115
|
||||||
|
#define B01110011 115
|
||||||
|
#define B1110100 116
|
||||||
|
#define B01110100 116
|
||||||
|
#define B1110101 117
|
||||||
|
#define B01110101 117
|
||||||
|
#define B1110110 118
|
||||||
|
#define B01110110 118
|
||||||
|
#define B1110111 119
|
||||||
|
#define B01110111 119
|
||||||
|
#define B1111000 120
|
||||||
|
#define B01111000 120
|
||||||
|
#define B1111001 121
|
||||||
|
#define B01111001 121
|
||||||
|
#define B1111010 122
|
||||||
|
#define B01111010 122
|
||||||
|
#define B1111011 123
|
||||||
|
#define B01111011 123
|
||||||
|
#define B1111100 124
|
||||||
|
#define B01111100 124
|
||||||
|
#define B1111101 125
|
||||||
|
#define B01111101 125
|
||||||
|
#define B1111110 126
|
||||||
|
#define B01111110 126
|
||||||
|
#define B1111111 127
|
||||||
|
#define B01111111 127
|
||||||
|
#define B10000000 128
|
||||||
|
#define B10000001 129
|
||||||
|
#define B10000010 130
|
||||||
|
#define B10000011 131
|
||||||
|
#define B10000100 132
|
||||||
|
#define B10000101 133
|
||||||
|
#define B10000110 134
|
||||||
|
#define B10000111 135
|
||||||
|
#define B10001000 136
|
||||||
|
#define B10001001 137
|
||||||
|
#define B10001010 138
|
||||||
|
#define B10001011 139
|
||||||
|
#define B10001100 140
|
||||||
|
#define B10001101 141
|
||||||
|
#define B10001110 142
|
||||||
|
#define B10001111 143
|
||||||
|
#define B10010000 144
|
||||||
|
#define B10010001 145
|
||||||
|
#define B10010010 146
|
||||||
|
#define B10010011 147
|
||||||
|
#define B10010100 148
|
||||||
|
#define B10010101 149
|
||||||
|
#define B10010110 150
|
||||||
|
#define B10010111 151
|
||||||
|
#define B10011000 152
|
||||||
|
#define B10011001 153
|
||||||
|
#define B10011010 154
|
||||||
|
#define B10011011 155
|
||||||
|
#define B10011100 156
|
||||||
|
#define B10011101 157
|
||||||
|
#define B10011110 158
|
||||||
|
#define B10011111 159
|
||||||
|
#define B10100000 160
|
||||||
|
#define B10100001 161
|
||||||
|
#define B10100010 162
|
||||||
|
#define B10100011 163
|
||||||
|
#define B10100100 164
|
||||||
|
#define B10100101 165
|
||||||
|
#define B10100110 166
|
||||||
|
#define B10100111 167
|
||||||
|
#define B10101000 168
|
||||||
|
#define B10101001 169
|
||||||
|
#define B10101010 170
|
||||||
|
#define B10101011 171
|
||||||
|
#define B10101100 172
|
||||||
|
#define B10101101 173
|
||||||
|
#define B10101110 174
|
||||||
|
#define B10101111 175
|
||||||
|
#define B10110000 176
|
||||||
|
#define B10110001 177
|
||||||
|
#define B10110010 178
|
||||||
|
#define B10110011 179
|
||||||
|
#define B10110100 180
|
||||||
|
#define B10110101 181
|
||||||
|
#define B10110110 182
|
||||||
|
#define B10110111 183
|
||||||
|
#define B10111000 184
|
||||||
|
#define B10111001 185
|
||||||
|
#define B10111010 186
|
||||||
|
#define B10111011 187
|
||||||
|
#define B10111100 188
|
||||||
|
#define B10111101 189
|
||||||
|
#define B10111110 190
|
||||||
|
#define B10111111 191
|
||||||
|
#define B11000000 192
|
||||||
|
#define B11000001 193
|
||||||
|
#define B11000010 194
|
||||||
|
#define B11000011 195
|
||||||
|
#define B11000100 196
|
||||||
|
#define B11000101 197
|
||||||
|
#define B11000110 198
|
||||||
|
#define B11000111 199
|
||||||
|
#define B11001000 200
|
||||||
|
#define B11001001 201
|
||||||
|
#define B11001010 202
|
||||||
|
#define B11001011 203
|
||||||
|
#define B11001100 204
|
||||||
|
#define B11001101 205
|
||||||
|
#define B11001110 206
|
||||||
|
#define B11001111 207
|
||||||
|
#define B11010000 208
|
||||||
|
#define B11010001 209
|
||||||
|
#define B11010010 210
|
||||||
|
#define B11010011 211
|
||||||
|
#define B11010100 212
|
||||||
|
#define B11010101 213
|
||||||
|
#define B11010110 214
|
||||||
|
#define B11010111 215
|
||||||
|
#define B11011000 216
|
||||||
|
#define B11011001 217
|
||||||
|
#define B11011010 218
|
||||||
|
#define B11011011 219
|
||||||
|
#define B11011100 220
|
||||||
|
#define B11011101 221
|
||||||
|
#define B11011110 222
|
||||||
|
#define B11011111 223
|
||||||
|
#define B11100000 224
|
||||||
|
#define B11100001 225
|
||||||
|
#define B11100010 226
|
||||||
|
#define B11100011 227
|
||||||
|
#define B11100100 228
|
||||||
|
#define B11100101 229
|
||||||
|
#define B11100110 230
|
||||||
|
#define B11100111 231
|
||||||
|
#define B11101000 232
|
||||||
|
#define B11101001 233
|
||||||
|
#define B11101010 234
|
||||||
|
#define B11101011 235
|
||||||
|
#define B11101100 236
|
||||||
|
#define B11101101 237
|
||||||
|
#define B11101110 238
|
||||||
|
#define B11101111 239
|
||||||
|
#define B11110000 240
|
||||||
|
#define B11110001 241
|
||||||
|
#define B11110010 242
|
||||||
|
#define B11110011 243
|
||||||
|
#define B11110100 244
|
||||||
|
#define B11110101 245
|
||||||
|
#define B11110110 246
|
||||||
|
#define B11110111 247
|
||||||
|
#define B11111000 248
|
||||||
|
#define B11111001 249
|
||||||
|
#define B11111010 250
|
||||||
|
#define B11111011 251
|
||||||
|
#define B11111100 252
|
||||||
|
#define B11111101 253
|
||||||
|
#define B11111110 254
|
||||||
|
#define B11111111 255
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Client.h - Base class that provides Client
|
||||||
|
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Stream.h"
|
||||||
|
#include "IPAddress.h"
|
||||||
|
|
||||||
|
class Client : public Stream {
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual int connect(IPAddress ip, uint16_t port) = 0;
|
||||||
|
virtual int connect(const char *host, uint16_t port) = 0;
|
||||||
|
virtual size_t write(uint8_t) = 0;
|
||||||
|
virtual size_t write(const uint8_t *buf, size_t size) = 0;
|
||||||
|
virtual int available() = 0;
|
||||||
|
virtual int read() = 0;
|
||||||
|
virtual int read(uint8_t *buf, size_t size) = 0;
|
||||||
|
virtual int peek() = 0;
|
||||||
|
virtual void flush() = 0;
|
||||||
|
virtual void stop() = 0;
|
||||||
|
virtual uint8_t connected() = 0;
|
||||||
|
virtual operator bool() = 0;
|
||||||
|
protected:
|
||||||
|
uint8_t *rawIPAddress(IPAddress &addr) {
|
||||||
|
return addr.raw_address();
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
/* C++ prototypes */
|
||||||
|
long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
||||||
|
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t makeWord(uint16_t w) {
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
uint16_t makeWord(uint8_t h, uint8_t l) {
|
||||||
|
return (h << 8) | l;
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void yield(void);
|
||||||
|
|
||||||
|
#define LOW 0 /* used for digitalWrite(), digitalRead(), openDrain() and attachInterrupt() */
|
||||||
|
#define HIGH 1 /* used for digitalWrite(), digitalRead(). There is no option for HIGH level interrupt provided by the hardware */
|
||||||
|
#define FLOATING HIGH /* Not in official API - used for openDrain, while making clear what you're doing */
|
||||||
|
#define CHANGE 4 /* used for attachInterrupt() */
|
||||||
|
#define FALLING 2 /* used for attachInterrupt() */
|
||||||
|
#define RISING 3 /* used for attachInterrupt() */
|
||||||
|
#define INPUT 0 /* used for pinMode() */
|
||||||
|
#define OUTPUT 1 /* used for pinMode() */
|
||||||
|
#define INPUT_PULLUP 2 /* used for pinMode() */
|
||||||
|
#define LSBFIRST 0 /* used for shiftIn/shiftOut */
|
||||||
|
#define MSBFIRST 1 /* used for shiftIn/shiftOut */
|
||||||
|
|
||||||
|
|
||||||
|
#define PI 3.1415926535897932384626433832795
|
||||||
|
#define HALF_PI 1.5707963267948966192313216916398
|
||||||
|
#define TWO_PI 6.283185307179586476925286766559
|
||||||
|
#define DEG_TO_RAD 0.017453292519943295769236907684886
|
||||||
|
#define RAD_TO_DEG 57.295779513082320876798154814105
|
||||||
|
#define EULER 2.718281828459045235360287471352
|
||||||
|
|
||||||
|
#define SERIAL 0x00
|
||||||
|
#define DISPLAY 0x01
|
||||||
|
|
||||||
|
#define min(a,b) \
|
||||||
|
({ __typeof__ (a) _a = (a); \
|
||||||
|
__typeof__ (b) _b = (b); \
|
||||||
|
_a < _b ? _a : _b; })
|
||||||
|
|
||||||
|
#define max(a,b) \
|
||||||
|
({ __typeof__ (a) _a = (a); \
|
||||||
|
__typeof__ (b) _b = (b); \
|
||||||
|
_a > _b ? _a : _b; })
|
||||||
|
|
||||||
|
#ifndef constrain
|
||||||
|
#define constrain(x,low,high) ({ \
|
||||||
|
__typeof__ (x) _x = (x); \
|
||||||
|
__typeof__ (low) _l = (low); \
|
||||||
|
__typeof__ (high) _h = (high); \
|
||||||
|
_x < _l ? _l : _x > _h ? _h :_x ;})
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef radians
|
||||||
|
#define radians(deg) ((deg)*DEG_TO_RAD)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef degrees
|
||||||
|
#define degrees(rad) ((rad)*RAD_TO_DEG)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef sq
|
||||||
|
#define sq(x) ({ __typeof__ (x) _x = (x); _x * _x; })
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* This is sorta silly. But normal round() builtin is bulkier and returns a float while arduino code written for other platforms will expect
|
||||||
|
integer types... This also has the same concerns about sideffects as before - except they're less likely to show up when
|
||||||
|
starting from a float. Who does meFloat++? Certainly not as many as do constrain(myInt++,minval,maxval);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef round
|
||||||
|
#define round(x) ({ __typeof__ (x) _x = (x); _x >= 0 ? (long)(_x + 0.5) : (long)(_x - 0.5 );})
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* In the end, it is probably better to do this manually rather than using round(). Why?
|
||||||
|
|
||||||
|
* If you know the value you're starting with is positive, you can skip the first test and just add 0.5 and cast to integer type - saves space.
|
||||||
|
* You can use the minimum size datatypes (wthis casts things to 4-byte values. Maybe the thing your rounding is, say -50.0 to 50.0 you can cast to a single byte for more compact code
|
||||||
|
* You can choose which of the rounding strategies you wish to use. Because there are not less than TEN DIFFERENT STRATEGIES TO ROUND NUMBERS
|
||||||
|
* Any of these approaches will result in smaller code:
|
||||||
|
* For example, rounding a number you know ought to fit a 16 bit unsigned datatype easily, you might do:
|
||||||
|
* uint16_t roundedval = (uint16_t) (floatval + 0.5)
|
||||||
|
*
|
||||||
|
* Even better, avoid using floats in the first place. Why are you using them? sometimes you really don't have much of a choice, other times
|
||||||
|
* you very definitely do. This is particularly true with ADC readings, for example say you're using to 4.096 Volt reference, and you
|
||||||
|
* want to measure and print out a voltage.
|
||||||
|
uint32_t reading = analogReadEnh(adc_pin, 12); //where adc_pin is just the pin number being read
|
||||||
|
float temp = reading;
|
||||||
|
Serial.print(temp/1000); // 4.096v reference, and 12 bit accuracy (ie, 0-4095). So each LSB is 1 mv so dividing by 1000 gets volts.
|
||||||
|
Serial.println('V'); //print out the units too
|
||||||
|
3.2k on a 1626/
|
||||||
|
Or you could do:
|
||||||
|
uint32_t reading = analogReadEnh(adc_pin, 12); // same analog reading
|
||||||
|
Serial.print(reading/1000) //volts
|
||||||
|
Serial.print('.'); //decimal point
|
||||||
|
Serial.print(reading%1000) //millivolts
|
||||||
|
Serial.println('V'); //print out the units too
|
||||||
|
// This would print the same thing, only without any floating point derived error... weighing in at 2k of flash.
|
||||||
|
|
||||||
|
// And just doing this saves another 100 bytes:
|
||||||
|
uint32_t reading = analogReadEnh(1, 12); //where adc_pin is just the pin number being read
|
||||||
|
Serial.print((uint16_t)reading);
|
||||||
|
Serial.println("mV");
|
||||||
|
|
||||||
|
|
||||||
|
* Yeah the last one is kind of a copoout, but not only is it flash-efficient, it also runs *much* faster, because division,
|
||||||
|
* be it floating point or otherwise, is sloowwwwwwww on AVR. But the fact that there are better ways to round than round()
|
||||||
|
* was not an excuse for it being broken like that.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef void (*voidFuncPtr)(void);
|
||||||
|
|
||||||
|
// interrupts() / noInterrupts() must be defined by the core
|
||||||
|
|
||||||
|
// a bunch of really silly hand-holding macros!
|
||||||
|
#define lowByte(w) ((uint8_t) ((w) & 0xff))
|
||||||
|
#define highByte(w) ((uint8_t) ((w) >> 8))
|
||||||
|
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
|
||||||
|
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
|
||||||
|
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
|
||||||
|
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
|
||||||
|
#ifndef bit
|
||||||
|
#define bit(b) (1UL << (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DISCOURAGE_NONSTANDARD_TYPES) && DISCOURAGE_NONSTANDARD_TYPES
|
||||||
|
typedef bool boolean __attribute__ ((deprecated("a 'boolean' is called a 'bool' in standard C - suggest using standard type name")));
|
||||||
|
typedef uint8_t byte __attribute__ ((deprecated("a 'byte' is an 'unsigned char' (may be more than 8 bits on non-AVR) or 'uint8_t' (always 8 bits) in standard C - suggest using one of these standard types.")));
|
||||||
|
typedef uint16_t word __attribute__ ((deprecated("a 'word' is an 'unsigned int' (may be more than 16 bits on non-AVR) or 'uint15_t' (always 16 bits) in standard C - suggest using one of these standard types.")));
|
||||||
|
#else
|
||||||
|
typedef bool boolean;
|
||||||
|
typedef uint8_t byte;
|
||||||
|
typedef uint16_t word;
|
||||||
|
#endif
|
||||||
|
void init(void);
|
||||||
|
void initVariant(void);
|
||||||
|
|
||||||
|
int atexit(void (*func)()) __attribute__((weak));
|
||||||
|
int main() __attribute__((weak));
|
||||||
|
|
||||||
|
#ifdef EXTENDED_PIN_MODE
|
||||||
|
// Platforms who want to declare more than 256 pins need to define EXTENDED_PIN_MODE globally
|
||||||
|
typedef uint32_t pin_size_t;
|
||||||
|
#else
|
||||||
|
typedef uint8_t pin_size_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void pinMode( pin_size_t pinNumber, uint8_t mode);
|
||||||
|
void digitalWrite( pin_size_t pinNumber, uint8_t val);
|
||||||
|
int8_t digitalRead( pin_size_t pinNumber);
|
||||||
|
void analogWrite( pin_size_t pinNumber, int value);
|
||||||
|
int analogRead( pin_size_t pinNumber);
|
||||||
|
void analogReference( uint8_t mode);
|
||||||
|
bool analogReadResolution( uint8_t res);
|
||||||
|
|
||||||
|
unsigned long millis(void);
|
||||||
|
unsigned long micros(void);
|
||||||
|
|
||||||
|
void delay(unsigned long);
|
||||||
|
void delayMicroseconds(unsigned int us);
|
||||||
|
unsigned long pulseIn(pin_size_t pin, uint8_t state, unsigned long timeout);
|
||||||
|
unsigned long pulseInLong(pin_size_t pin, uint8_t state, unsigned long timeout);
|
||||||
|
|
||||||
|
void shiftOut(pin_size_t dataPin, pin_size_t clockPin, uint8_t bitOrder, uint8_t val);
|
||||||
|
uint8_t shiftIn(pin_size_t dataPin, pin_size_t clockPin, uint8_t bitOrder);
|
||||||
|
|
||||||
|
void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, uint8_t mode);
|
||||||
|
void detachInterrupt(pin_size_t interruptNumber);
|
||||||
|
|
||||||
|
void setup(void);
|
||||||
|
void loop(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
/* C++ prototypes */
|
||||||
|
uint16_t makeWord(uint16_t w);
|
||||||
|
uint16_t makeWord(byte h, byte l);
|
||||||
|
|
||||||
|
#define word(...) makeWord(__VA_ARGS__)
|
||||||
|
|
||||||
|
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
|
||||||
|
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
|
||||||
|
|
||||||
|
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0);
|
||||||
|
void noTone(uint8_t _pin);
|
||||||
|
|
||||||
|
// WMath prototypes
|
||||||
|
long random(long);
|
||||||
|
long random(long, long);
|
||||||
|
void randomSeed(unsigned long);
|
||||||
|
long map(long, long, long, long, long);
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
/* THIS FILE IS NOT USED BY ANY PART OF megaTinyCore
|
||||||
|
* Wire subclasses Stream directly, saving a large amount of flash
|
||||||
|
* The overhead was additive, and made the Wire.h unusable 2/4k parts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "Stream.h"
|
||||||
|
|
||||||
|
class HardwareI2C : public Stream {
|
||||||
|
public:
|
||||||
|
virtual void begin() = 0;
|
||||||
|
virtual void begin(uint8_t address) = 0;
|
||||||
|
virtual void end() = 0;
|
||||||
|
|
||||||
|
virtual void setClock(uint32_t freq) = 0;
|
||||||
|
|
||||||
|
virtual void beginTransmission(uint8_t address) = 0;
|
||||||
|
virtual uint8_t endTransmission(bool stopBit) = 0;
|
||||||
|
virtual uint8_t endTransmission(void) = 0;
|
||||||
|
|
||||||
|
virtual uint8_t requestFrom(uint8_t address, size_t len, bool stopBit) = 0;
|
||||||
|
virtual uint8_t requestFrom(uint8_t address, size_t len) = 0;
|
||||||
|
|
||||||
|
virtual void onReceive(void(*)(int)) = 0;
|
||||||
|
virtual void onRequest(void(*)(void)) = 0;
|
||||||
|
};
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Modified Spence Konde 2021 - constants removed.
|
||||||
|
Modified Spence Konde 2022 - commemt out the entire class. .
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#prama never
|
||||||
|
/*
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "Stream.h"
|
||||||
|
|
||||||
|
// XXX: Those constants should be defined as const int / enums?
|
||||||
|
// XXX: shall we use namespaces too?
|
||||||
|
// Spence: Screw that noise!
|
||||||
|
// We do not have flash to waste, this code will be run with parts that have only 4k of flash
|
||||||
|
// so using an integer to hold settings, and then REARRANGING THE BITS AT RUNTIME,
|
||||||
|
// when they are ALL LOCATED NEXT TO EACH OTHER ON THE SAME BYTE is madness!
|
||||||
|
// See UART_constants.h
|
||||||
|
|
||||||
|
class HardwareSerial : public Stream {
|
||||||
|
public:
|
||||||
|
virtual void begin(unsigned long) = 0;
|
||||||
|
virtual void begin(unsigned long baudrate, uint16_t config) = 0;
|
||||||
|
virtual void end() = 0;
|
||||||
|
virtual int available(void) = 0;
|
||||||
|
virtual int peek(void) = 0;
|
||||||
|
virtual int read(void) = 0;
|
||||||
|
virtual void flush(void) = 0;
|
||||||
|
virtual size_t write(uint8_t) = 0;
|
||||||
|
using Print::write; // pull in write(str) and write(buf, size) from Print
|
||||||
|
virtual operator bool() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// XXX: Are we keeping the serialEvent API?
|
||||||
|
// Spence: Apparently not.
|
||||||
|
*/
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
IPAddress.cpp - Base class that provides IPAddress
|
||||||
|
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IPAddress.h"
|
||||||
|
#include "Print.h"
|
||||||
|
|
||||||
|
IPAddress::IPAddress() {
|
||||||
|
_address.dword = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) {
|
||||||
|
_address.bytes[0] = first_octet;
|
||||||
|
_address.bytes[1] = second_octet;
|
||||||
|
_address.bytes[2] = third_octet;
|
||||||
|
_address.bytes[3] = fourth_octet;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(uint32_t address) {
|
||||||
|
_address.dword = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(const uint8_t *address) {
|
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPAddress::fromString(const char *address) {
|
||||||
|
// TODO: add support for "a", "a.b", "a.b.c" formats
|
||||||
|
|
||||||
|
uint16_t acc = 0; // Accumulator
|
||||||
|
uint8_t dots = 0;
|
||||||
|
|
||||||
|
while (*address) {
|
||||||
|
char c = *address++;
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
acc = acc * 10 + (c - '0');
|
||||||
|
if (acc > 255) {
|
||||||
|
// Value out of [0..255] range
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (c == '.') {
|
||||||
|
if (dots == 3) {
|
||||||
|
// Too much dots (there must be 3 dots)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_address.bytes[dots++] = acc;
|
||||||
|
acc = 0;
|
||||||
|
} else {
|
||||||
|
// Invalid char
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dots != 3) {
|
||||||
|
// Too few dots (there must be 3 dots)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_address.bytes[3] = acc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress &IPAddress::operator = (const uint8_t *address) {
|
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress &IPAddress::operator = (uint32_t address) {
|
||||||
|
_address.dword = address;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPAddress::operator==(const uint8_t *addr) const {
|
||||||
|
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t IPAddress::printTo(Print &p) const {
|
||||||
|
size_t n = 0;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
n += p.print(_address.bytes[i], DEC);
|
||||||
|
n += p.print('.');
|
||||||
|
}
|
||||||
|
n += p.print(_address.bytes[3], DEC);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IPAddress INADDR_NONE(0, 0, 0, 0);
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
IPAddress.h - Base class that provides IPAddress
|
||||||
|
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "Printable.h"
|
||||||
|
#include "String.h"
|
||||||
|
|
||||||
|
// A class to make it easier to handle and pass around IP addresses
|
||||||
|
|
||||||
|
class IPAddress : public Printable {
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
uint8_t bytes[4]; // IPv4 address
|
||||||
|
uint32_t dword;
|
||||||
|
} _address;
|
||||||
|
|
||||||
|
// Access the raw byte array containing the address. Because this returns a pointer
|
||||||
|
// to the internal structure rather than a copy of the address this function should only
|
||||||
|
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||||
|
// stored.
|
||||||
|
uint8_t *raw_address() {
|
||||||
|
return _address.bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructors
|
||||||
|
IPAddress();
|
||||||
|
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||||
|
IPAddress(uint32_t address);
|
||||||
|
IPAddress(const uint8_t *address);
|
||||||
|
|
||||||
|
bool fromString(const char *address);
|
||||||
|
bool fromString(const String &address) {
|
||||||
|
return fromString(address.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
|
||||||
|
// to a four-byte uint8_t array is expected
|
||||||
|
operator uint32_t() const {
|
||||||
|
return _address.dword;
|
||||||
|
};
|
||||||
|
bool operator==(const IPAddress &addr) const {
|
||||||
|
return _address.dword == addr._address.dword;
|
||||||
|
};
|
||||||
|
bool operator==(const uint8_t *addr) const;
|
||||||
|
|
||||||
|
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||||
|
uint8_t operator[](int index) const {
|
||||||
|
return _address.bytes[index];
|
||||||
|
};
|
||||||
|
uint8_t &operator[](int index) {
|
||||||
|
return _address.bytes[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
|
||||||
|
IPAddress &operator = (const uint8_t *address);
|
||||||
|
IPAddress &operator = (uint32_t address);
|
||||||
|
|
||||||
|
virtual size_t printTo(Print &p) const;
|
||||||
|
|
||||||
|
friend class EthernetClass;
|
||||||
|
friend class UDP;
|
||||||
|
friend class Client;
|
||||||
|
friend class Server;
|
||||||
|
friend class DhcpClass;
|
||||||
|
friend class DNSClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const IPAddress INADDR_NONE;
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
PluggableUSB.cpp
|
||||||
|
Copyright (c) 2015 Arduino LLC
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "USBAPI.h"
|
||||||
|
#include "PluggableUSB.h"
|
||||||
|
|
||||||
|
int PluggableUSB_::getInterface(uint8_t *interfaceCount) {
|
||||||
|
int sent = 0;
|
||||||
|
PluggableUSBModule *node;
|
||||||
|
for (node = rootNode; node; node = node->next) {
|
||||||
|
int res = node->getInterface(interfaceCount);
|
||||||
|
if (res < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sent += res;
|
||||||
|
}
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PluggableUSB_::getDescriptor(USBSetup &setup) {
|
||||||
|
PluggableUSBModule *node;
|
||||||
|
for (node = rootNode; node; node = node->next) {
|
||||||
|
int ret = node->getDescriptor(setup);
|
||||||
|
// ret!=0 -> request has been processed
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluggableUSB_::getShortName(char *iSerialNum) {
|
||||||
|
PluggableUSBModule *node;
|
||||||
|
for (node = rootNode; node; node = node->next) {
|
||||||
|
iSerialNum += node->getShortName(iSerialNum);
|
||||||
|
}
|
||||||
|
*iSerialNum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluggableUSB_::setup(USBSetup &setup) {
|
||||||
|
PluggableUSBModule *node;
|
||||||
|
for (node = rootNode; node; node = node->next) {
|
||||||
|
if (node->setup(setup)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluggableUSB_::plug(PluggableUSBModule *node) {
|
||||||
|
if ((lastEp + node->numEndpoints) > totalEP) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rootNode) {
|
||||||
|
rootNode = node;
|
||||||
|
} else {
|
||||||
|
PluggableUSBModule *current = rootNode;
|
||||||
|
while (current->next) {
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
current->next = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->pluggedInterface = lastIf;
|
||||||
|
node->pluggedEndpoint = lastEp;
|
||||||
|
lastIf += node->numInterfaces;
|
||||||
|
for (uint8_t i = 0; i < node->numEndpoints; i++) {
|
||||||
|
*(unsigned int *)(epBuffer(lastEp)) = node->endpointType[i];
|
||||||
|
lastEp++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
// restart USB layer???
|
||||||
|
}
|
||||||
|
|
||||||
|
PluggableUSB_ &PluggableUSB() {
|
||||||
|
static PluggableUSB_ obj;
|
||||||
|
return obj;
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
PluggableUSB.h
|
||||||
|
Copyright (c) 2015 Arduino LLC
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PUSB_h
|
||||||
|
#define PUSB_h
|
||||||
|
|
||||||
|
#include "USBAPI.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// core need to define
|
||||||
|
void *epBuffer(unsigned int n); // -> returns a pointer to the Nth element of the EP buffer structure
|
||||||
|
|
||||||
|
class PluggableUSBModule {
|
||||||
|
public:
|
||||||
|
PluggableUSBModule(uint8_t numEps, uint8_t numIfs, unsigned int *epType) :
|
||||||
|
numEndpoints(numEps), numInterfaces(numIfs), endpointType(epType)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool setup(USBSetup &setup) = 0;
|
||||||
|
virtual int getInterface(uint8_t *interfaceCount) = 0;
|
||||||
|
virtual int getDescriptor(USBSetup &setup) = 0;
|
||||||
|
virtual uint8_t getShortName(char *name) {
|
||||||
|
name[0] = 'A' + pluggedInterface;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pluggedInterface;
|
||||||
|
uint8_t pluggedEndpoint;
|
||||||
|
|
||||||
|
const uint8_t numEndpoints;
|
||||||
|
const uint8_t numInterfaces;
|
||||||
|
const unsigned int *endpointType;
|
||||||
|
|
||||||
|
PluggableUSBModule *next = NULL;
|
||||||
|
|
||||||
|
friend class PluggableUSB_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PluggableUSB_ {
|
||||||
|
public:
|
||||||
|
PluggableUSB_();
|
||||||
|
bool plug(PluggableUSBModule *node);
|
||||||
|
int getInterface(uint8_t *interfaceCount);
|
||||||
|
int getDescriptor(USBSetup &setup);
|
||||||
|
bool setup(USBSetup &setup);
|
||||||
|
void getShortName(char *iSerialNum);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t lastIf;
|
||||||
|
uint8_t lastEp;
|
||||||
|
PluggableUSBModule *rootNode;
|
||||||
|
uint8_t totalEP;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Replacement for global singleton.
|
||||||
|
// This function prevents static-initialization-order-fiasco
|
||||||
|
// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
|
||||||
|
PluggableUSB_ &PluggableUSB();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,285 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 Arduino. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "Print.h"
|
||||||
|
|
||||||
|
// Public Methods //////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/* default implementation: may be overridden */
|
||||||
|
size_t Print::write(const uint8_t *buffer, size_t size) {
|
||||||
|
size_t n = 0;
|
||||||
|
while (size--) {
|
||||||
|
if (write(*buffer++)) {
|
||||||
|
n++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(const __FlashStringHelper *ifsh) {
|
||||||
|
#if defined(__AVR__)
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(ifsh);
|
||||||
|
size_t n = 0;
|
||||||
|
while (1) {
|
||||||
|
unsigned char c = pgm_read_byte(p++);
|
||||||
|
if (c == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (write(c)) {
|
||||||
|
n++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
#else
|
||||||
|
return print(reinterpret_cast<const char *>(ifsh));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(const String &s) {
|
||||||
|
return write(s.c_str(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(const char str[]) {
|
||||||
|
return write(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(char c) {
|
||||||
|
return write(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(unsigned char b, int base) {
|
||||||
|
return print((unsigned long) b, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(int n, int base) {
|
||||||
|
return print((long) n, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(unsigned int n, int base) {
|
||||||
|
return print((unsigned long) n, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(long n, int base) {
|
||||||
|
if (base == 0) {
|
||||||
|
return write(n);
|
||||||
|
} else if (base == 10) {
|
||||||
|
if (n < 0) {
|
||||||
|
int t = print('-');
|
||||||
|
n = -n;
|
||||||
|
return printNumber(n, 10) + t;
|
||||||
|
}
|
||||||
|
return printNumber(n, 10);
|
||||||
|
} else {
|
||||||
|
return printNumber(n, base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(unsigned long n, int base) {
|
||||||
|
if (base == 0) {
|
||||||
|
return write(n);
|
||||||
|
} else {
|
||||||
|
return printNumber(n, base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(double n, int digits) {
|
||||||
|
return printFloat(n, digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(const __FlashStringHelper *ifsh) {
|
||||||
|
size_t n = print(ifsh);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::print(const Printable &x) {
|
||||||
|
return x.printTo(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(void) {
|
||||||
|
return write("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(const String &s) {
|
||||||
|
size_t n = print(s);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(const char c[]) {
|
||||||
|
size_t n = print(c);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(char c) {
|
||||||
|
size_t n = print(c);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(unsigned char b, int base) {
|
||||||
|
size_t n = print(b, base);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(int num, int base) {
|
||||||
|
size_t n = print(num, base);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(unsigned int num, int base) {
|
||||||
|
size_t n = print(num, base);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(long num, int base) {
|
||||||
|
size_t n = print(num, base);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(unsigned long num, int base) {
|
||||||
|
size_t n = print(num, base);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(double num, int digits) {
|
||||||
|
size_t n = print(num, digits);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::println(const Printable &x) {
|
||||||
|
size_t n = print(x);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom implementation of printf borrowed from the teensy core files
|
||||||
|
static int16_t printf_putchar(char c, FILE *fp) {
|
||||||
|
((class Print *)(fdev_get_udata(fp)))->write((uint8_t)c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t Print::printf(const char *format, ...) {
|
||||||
|
FILE f;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||||
|
fdev_set_udata(&f, this);
|
||||||
|
va_start(ap, format);
|
||||||
|
return vfprintf(&f, format, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t Print::printf(const __FlashStringHelper *format, ...) {
|
||||||
|
FILE f;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||||
|
fdev_set_udata(&f, this);
|
||||||
|
va_start(ap, format);
|
||||||
|
return vfprintf_P(&f, (const char *)format, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private Methods /////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
size_t Print::printNumber(unsigned long n, uint8_t base) {
|
||||||
|
char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
|
||||||
|
char *str = &buf[sizeof(buf) - 1];
|
||||||
|
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
// prevent crash if called with base == 1
|
||||||
|
if (base < 2) {
|
||||||
|
base = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
char c = n % base;
|
||||||
|
n /= base;
|
||||||
|
|
||||||
|
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||||||
|
} while (n);
|
||||||
|
|
||||||
|
return write(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Print::printFloat(double number, uint8_t digits) {
|
||||||
|
size_t n = 0;
|
||||||
|
|
||||||
|
if (isnan(number)) {
|
||||||
|
return print("nan");
|
||||||
|
}
|
||||||
|
if (isinf(number)) {
|
||||||
|
return print("inf");
|
||||||
|
}
|
||||||
|
if (number > 4294967040.0) {
|
||||||
|
return print("ovf"); // constant determined empirically
|
||||||
|
}
|
||||||
|
if (number < -4294967040.0) {
|
||||||
|
return print("ovf"); // constant determined empirically
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle negative numbers
|
||||||
|
if (number < 0.0) {
|
||||||
|
n += print('-');
|
||||||
|
number = -number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||||
|
double rounding = 0.5;
|
||||||
|
for (uint8_t i = 0; i < digits; ++i) {
|
||||||
|
rounding /= 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
number += rounding;
|
||||||
|
|
||||||
|
// Extract the integer part of the number and print it
|
||||||
|
unsigned long int_part = (unsigned long)number;
|
||||||
|
double remainder = number - (double)int_part;
|
||||||
|
n += print(int_part);
|
||||||
|
|
||||||
|
// Print the decimal point, but only if there are digits beyond
|
||||||
|
if (digits > 0) {
|
||||||
|
n += print(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract digits from the remainder one at a time
|
||||||
|
while (digits-- > 0) {
|
||||||
|
remainder *= 10.0;
|
||||||
|
unsigned int toPrint = (unsigned int)remainder;
|
||||||
|
n += print(toPrint);
|
||||||
|
remainder -= toPrint;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h> // for size_t
|
||||||
|
|
||||||
|
#include "String.h"
|
||||||
|
#include "Printable.h"
|
||||||
|
|
||||||
|
#define DEC 10
|
||||||
|
#define HEX 16
|
||||||
|
#define OCT 8
|
||||||
|
#define BIN 2
|
||||||
|
|
||||||
|
class Print {
|
||||||
|
private:
|
||||||
|
int write_error;
|
||||||
|
size_t printNumber(unsigned long, uint8_t);
|
||||||
|
size_t printFloat(double, uint8_t);
|
||||||
|
protected:
|
||||||
|
void setWriteError(int err = 1) {
|
||||||
|
write_error = err;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
Print() : write_error(0) {}
|
||||||
|
|
||||||
|
int getWriteError() {
|
||||||
|
return write_error;
|
||||||
|
}
|
||||||
|
void clearWriteError() {
|
||||||
|
setWriteError(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t write(uint8_t) = 0;
|
||||||
|
size_t write(const char *str) {
|
||||||
|
if (str == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return write((const uint8_t *)str, strlen(str));
|
||||||
|
}
|
||||||
|
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||||
|
size_t write(const char *buffer, size_t size) {
|
||||||
|
return write((const uint8_t *)buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// default to zero, meaning "a single write may block"
|
||||||
|
// should be overridden by subclasses with buffering
|
||||||
|
virtual int availableForWrite() { return 0; }
|
||||||
|
|
||||||
|
size_t print(const __FlashStringHelper *);
|
||||||
|
size_t print(const String &);
|
||||||
|
size_t print(const char[]);
|
||||||
|
size_t print(char);
|
||||||
|
size_t print(unsigned char, int = DEC);
|
||||||
|
size_t print(int, int = DEC);
|
||||||
|
size_t print(unsigned int, int = DEC);
|
||||||
|
size_t print(long, int = DEC);
|
||||||
|
size_t print(unsigned long, int = DEC);
|
||||||
|
size_t print(double, int = 2);
|
||||||
|
size_t print(const Printable &);
|
||||||
|
|
||||||
|
size_t println(const __FlashStringHelper *);
|
||||||
|
size_t println(const String &s);
|
||||||
|
size_t println(const char[]);
|
||||||
|
size_t println(char);
|
||||||
|
size_t println(unsigned char, int = DEC);
|
||||||
|
size_t println(int, int = DEC);
|
||||||
|
size_t println(unsigned int, int = DEC);
|
||||||
|
size_t println(long, int = DEC);
|
||||||
|
size_t println(unsigned long, int = DEC);
|
||||||
|
size_t println(double, int = 2);
|
||||||
|
size_t println(const Printable &);
|
||||||
|
size_t println(void);
|
||||||
|
|
||||||
|
int16_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
|
int16_t printf(const __FlashStringHelper *format, ...);
|
||||||
|
|
||||||
|
virtual void flush() { /* Empty implementation for backward compatibility */ }
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
class Print;
|
||||||
|
|
||||||
|
/** The Printable class provides a way for new classes to allow themselves to be printed.
|
||||||
|
By deriving from Printable and implementing the printTo method, it will then be possible
|
||||||
|
for users to print out instances of this class by passing them into the usual
|
||||||
|
Print::print and Print::println methods.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Printable {
|
||||||
|
public:
|
||||||
|
virtual size_t printTo(Print &p) const = 0;
|
||||||
|
};
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 Arduino. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RingBuffer.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
RingBuffer::RingBuffer(rb_index_type size) : size(size) {
|
||||||
|
_aucBuffer = (uint8_t *)malloc(size);
|
||||||
|
memset(_aucBuffer, 0, size) ;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RingBuffer::store_char(uint8_t c) {
|
||||||
|
rb_index_type i = nextIndex(_iHead);
|
||||||
|
|
||||||
|
// if we should be storing the received character into the location
|
||||||
|
// just before the tail (meaning that the head would advance to the
|
||||||
|
// current location of the tail), we're about to overflow the buffer
|
||||||
|
// and so we don't write the character or advance the head.
|
||||||
|
if (i != _iTail) {
|
||||||
|
if (_iHead < size) {
|
||||||
|
_aucBuffer[_iHead] = c ;
|
||||||
|
} else {
|
||||||
|
additionalBuffer[_iHead - size] = c;
|
||||||
|
}
|
||||||
|
_iHead = i ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RingBuffer::clear() {
|
||||||
|
_iHead = 0;
|
||||||
|
_iTail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RingBuffer::read_char() {
|
||||||
|
if (_iTail == _iHead) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t value;
|
||||||
|
if (_iTail < size) {
|
||||||
|
value = _aucBuffer[_iTail];
|
||||||
|
} else {
|
||||||
|
value = additionalBuffer[_iTail - size];
|
||||||
|
}
|
||||||
|
_iTail = nextIndex(_iTail);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RingBuffer::available() {
|
||||||
|
int delta = _iHead - _iTail;
|
||||||
|
|
||||||
|
if (delta < 0) {
|
||||||
|
return size + additionalSize + delta;
|
||||||
|
} else {
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int RingBuffer::availableForStore() {
|
||||||
|
int delta = _iHead - _iTail;
|
||||||
|
if (delta >= 0) {
|
||||||
|
return size + additionalSize - 1 - delta;
|
||||||
|
} else {
|
||||||
|
return -delta - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int RingBuffer::peek() {
|
||||||
|
if (_iTail == _iHead) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_iTail < size) {
|
||||||
|
return _aucBuffer[_iTail];
|
||||||
|
} else {
|
||||||
|
return additionalBuffer[_iTail - size];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_index_type RingBuffer::nextIndex(rb_index_type index) {
|
||||||
|
return (rb_index_type)(index + 1) % (size + additionalSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RingBuffer::isFull() {
|
||||||
|
return (nextIndex(_iHead) == _iTail);
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 Arduino. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RING_BUFFER_
|
||||||
|
#define _RING_BUFFER_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Define constants and variables for buffering incoming serial data. We're
|
||||||
|
// using a ring buffer (I think), in which head is the index of the location
|
||||||
|
// to which to write the next incoming character and tail is the index of the
|
||||||
|
// location from which to read.
|
||||||
|
|
||||||
|
#define RINGBUFFER_HAS_ADDITIONAL_STORAGE_API
|
||||||
|
|
||||||
|
#ifdef RINGBUFFER_FORCE_SMALL_SIZE
|
||||||
|
typedef uint8_t rb_index_type;
|
||||||
|
#else
|
||||||
|
typedef unsigned int rb_index_type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class RingBuffer {
|
||||||
|
public:
|
||||||
|
RingBuffer(rb_index_type size = 64) ;
|
||||||
|
void store_char(uint8_t c) ;
|
||||||
|
void clear();
|
||||||
|
int read_char();
|
||||||
|
int available();
|
||||||
|
int availableForStore();
|
||||||
|
int peek();
|
||||||
|
bool isFull();
|
||||||
|
void addStorage(uint8_t *_buffer, rb_index_type _size) {
|
||||||
|
additionalSize = _size;
|
||||||
|
additionalBuffer = _buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
rb_index_type nextIndex(rb_index_type index);
|
||||||
|
uint8_t *additionalBuffer;
|
||||||
|
int additionalSize = 0;
|
||||||
|
rb_index_type size;
|
||||||
|
uint8_t *_aucBuffer;
|
||||||
|
volatile rb_index_type _iHead ;
|
||||||
|
volatile rb_index_type _iTail ;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _RING_BUFFER_ */
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
Server.h - Base class that provides Server
|
||||||
|
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Print.h"
|
||||||
|
|
||||||
|
class Server : public Print {
|
||||||
|
public:
|
||||||
|
virtual void begin() = 0;
|
||||||
|
};
|
|
@ -0,0 +1,358 @@
|
||||||
|
/*
|
||||||
|
Stream.cpp - adds parsing methods to Stream class
|
||||||
|
Copyright (c) 2008 David A. Mellis. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Created July 2011
|
||||||
|
parsing functions based on TextFinder library by Michael Margolis
|
||||||
|
|
||||||
|
findMulti/findUntil routines written by Jim Leonard/Xuth
|
||||||
|
*/
|
||||||
|
#include "util/delay.h"
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Stream.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
|
||||||
|
/*
|
||||||
|
__attribute__ ((inline)) void wait_250_clocks() {
|
||||||
|
for (uint8_t i = 0; i < 245; i++) {
|
||||||
|
NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
int Stream::timedRead() {
|
||||||
|
#if !defined(MILLIS_USE_TIMERNONE)
|
||||||
|
int c;
|
||||||
|
unsigned long startMillis = millis(); //formerly, this was a member variable! Eating 4 bytes of ram for no bloody reason.
|
||||||
|
do {
|
||||||
|
c = read();
|
||||||
|
if (c >= 0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
} while (millis() - startMillis < _timeout);
|
||||||
|
return -1; // -1 indicates timeout
|
||||||
|
#else
|
||||||
|
int c;
|
||||||
|
for (uint32_t i = 0; i < _timeout; i++) {
|
||||||
|
c = read();
|
||||||
|
if (c >= 0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
_delay_us(980); //not 1000, b/c compensation for the rest
|
||||||
|
}
|
||||||
|
return -1; // -1 indicates timeout
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// private method to peek stream with timeout
|
||||||
|
int Stream::timedPeek() {
|
||||||
|
#if !defined(MILLIS_USE_TIMERNONE)
|
||||||
|
int c;
|
||||||
|
unsigned long startMillis = millis();
|
||||||
|
do {
|
||||||
|
c = peek();
|
||||||
|
if (c >= 0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
} while (millis() - startMillis < _timeout);
|
||||||
|
return -1; // -1 indicates timeout
|
||||||
|
#else
|
||||||
|
int c;
|
||||||
|
for (uint32_t i = 0; i < _timeout; i++) {
|
||||||
|
c = read();
|
||||||
|
if (c >= 0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
_delay_us(980); //not 1000, b/c compensation for the rest
|
||||||
|
}
|
||||||
|
return -1; // -1 indicates timeout
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns peek of the next digit in the stream or -1 if timeout
|
||||||
|
// discards non-numeric characters
|
||||||
|
int Stream::peekNextDigit(LookaheadMode lookahead, bool detectDecimal) {
|
||||||
|
int c;
|
||||||
|
while (1) {
|
||||||
|
c = timedPeek();
|
||||||
|
|
||||||
|
if (c < 0 ||
|
||||||
|
c == '-' ||
|
||||||
|
(c >= '0' && c <= '9') ||
|
||||||
|
(detectDecimal && c == '.')) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lookahead) {
|
||||||
|
case SKIP_NONE: return -1; // Fail code.
|
||||||
|
case SKIP_WHITESPACE:
|
||||||
|
switch (c) {
|
||||||
|
case ' ': // fall-through
|
||||||
|
case '\t':
|
||||||
|
case '\r':
|
||||||
|
case '\n': break;
|
||||||
|
default: return -1; // Fail code.
|
||||||
|
}
|
||||||
|
case SKIP_ALL:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
read(); // discard non-numeric
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Methods
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Stream::setTimeout(unsigned long timeout) { // sets the maximum number of milliseconds to wait
|
||||||
|
_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find returns true if the target string is found
|
||||||
|
bool Stream::find(char *target) {
|
||||||
|
return findUntil(target, strlen(target), NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reads data from the stream until the target string of given length is found
|
||||||
|
// returns true if target string is found, false if timed out
|
||||||
|
bool Stream::find(char *target, size_t length) {
|
||||||
|
return findUntil(target, length, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// as find but search ends if the terminator string is found
|
||||||
|
bool Stream::findUntil(char *target, char *terminator) {
|
||||||
|
return findUntil(target, strlen(target), terminator, strlen(terminator));
|
||||||
|
}
|
||||||
|
|
||||||
|
// reads data from the stream until the target string of the given length is found
|
||||||
|
// search terminated if the terminator string is found
|
||||||
|
// returns true if target string is found, false if terminated or timed out
|
||||||
|
bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen) {
|
||||||
|
if (terminator == NULL) {
|
||||||
|
MultiTarget t[1] = {{target, targetLen, 0}};
|
||||||
|
return findMulti(t, 1) == 0 ? true : false;
|
||||||
|
} else {
|
||||||
|
MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
|
||||||
|
return findMulti(t, 2) == 0 ? true : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the first valid (long) integer value from the current position.
|
||||||
|
// lookahead determines how parseInt looks ahead in the stream.
|
||||||
|
// See LookaheadMode enumeration at the top of the file.
|
||||||
|
// Lookahead is terminated by the first character that is not a valid part of an integer.
|
||||||
|
// Once parsing commences, 'ignore' will be skipped in the stream.
|
||||||
|
long Stream::parseInt(LookaheadMode lookahead, char ignore) {
|
||||||
|
bool isNegative = false;
|
||||||
|
long value = 0;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
c = peekNextDigit(lookahead, false);
|
||||||
|
// ignore non numeric leading characters
|
||||||
|
if (c < 0) {
|
||||||
|
return 0; // zero returned if timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (c == ignore)
|
||||||
|
; // ignore this character
|
||||||
|
else if (c == '-') {
|
||||||
|
isNegative = true;
|
||||||
|
} else if (c >= '0' && c <= '9') { // is c a digit?
|
||||||
|
value = value * 10 + c - '0';
|
||||||
|
}
|
||||||
|
read(); // consume the character we got with peek
|
||||||
|
c = timedPeek();
|
||||||
|
} while ((c >= '0' && c <= '9') || c == ignore);
|
||||||
|
|
||||||
|
if (isNegative) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// as parseInt but returns a floating point value
|
||||||
|
float Stream::parseFloat(LookaheadMode lookahead, char ignore) {
|
||||||
|
bool isNegative = false;
|
||||||
|
bool isFraction = false;
|
||||||
|
long value = 0;
|
||||||
|
int c;
|
||||||
|
float fraction = 1.0;
|
||||||
|
|
||||||
|
c = peekNextDigit(lookahead, true);
|
||||||
|
// ignore non numeric leading characters
|
||||||
|
if (c < 0) {
|
||||||
|
return 0; // zero returned if timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (c == ignore)
|
||||||
|
; // ignore
|
||||||
|
else if (c == '-') {
|
||||||
|
isNegative = true;
|
||||||
|
} else if (c == '.') {
|
||||||
|
isFraction = true;
|
||||||
|
} else if (c >= '0' && c <= '9') { // is c a digit?
|
||||||
|
value = value * 10 + c - '0';
|
||||||
|
if (isFraction) {
|
||||||
|
fraction *= 0.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
read(); // consume the character we got with peek
|
||||||
|
c = timedPeek();
|
||||||
|
} while ((c >= '0' && c <= '9') || (c == '.' && !isFraction) || c == ignore);
|
||||||
|
|
||||||
|
if (isNegative) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
if (isFraction) {
|
||||||
|
return value * fraction;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read characters from stream into buffer
|
||||||
|
// terminates if length characters have been read, or timeout (see setTimeout)
|
||||||
|
// returns the number of characters placed in the buffer
|
||||||
|
// the buffer is NOT null terminated.
|
||||||
|
//
|
||||||
|
size_t Stream::readBytes(char *buffer, size_t length) {
|
||||||
|
size_t count = 0;
|
||||||
|
while (count < length) {
|
||||||
|
int c = timedRead();
|
||||||
|
if (c < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*buffer++ = (char)c;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// as readBytes with terminator character
|
||||||
|
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||||
|
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||||
|
|
||||||
|
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) {
|
||||||
|
if (length < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t index = 0;
|
||||||
|
while (index < length) {
|
||||||
|
int c = timedRead();
|
||||||
|
if (c < 0 || c == terminator) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*buffer++ = (char)c;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return index; // return number of characters, not including null terminator
|
||||||
|
}
|
||||||
|
|
||||||
|
String Stream::readString() {
|
||||||
|
String ret;
|
||||||
|
int c = timedRead();
|
||||||
|
while (c >= 0) {
|
||||||
|
ret += (char)c;
|
||||||
|
c = timedRead();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Stream::readStringUntil(char terminator) {
|
||||||
|
String ret;
|
||||||
|
int c = timedRead();
|
||||||
|
while (c >= 0 && c != terminator) {
|
||||||
|
ret += (char)c;
|
||||||
|
c = timedRead();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Stream::findMulti(struct Stream::MultiTarget *targets, int tCount) {
|
||||||
|
// any zero length target string automatically matches and would make
|
||||||
|
// a mess of the rest of the algorithm.
|
||||||
|
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
|
||||||
|
if (t->len <= 0) {
|
||||||
|
return t - targets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int c = timedRead();
|
||||||
|
if (c < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
|
||||||
|
// the simple case is if we match, deal with that first.
|
||||||
|
if (c == t->str[t->index]) {
|
||||||
|
if (++t->index == t->len) {
|
||||||
|
return t - targets;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not we need to walk back and see if we could have matched further
|
||||||
|
// down the stream (ie '1112' doesn't match the first position in '11112'
|
||||||
|
// but it will match the second position so we can't just reset the current
|
||||||
|
// index to 0 when we find a mismatch.
|
||||||
|
if (t->index == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int origIndex = t->index;
|
||||||
|
do {
|
||||||
|
--t->index;
|
||||||
|
// first check if current char works against the new current index
|
||||||
|
if (c != t->str[t->index]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's the only char then we're good, nothing more to check
|
||||||
|
if (t->index == 0) {
|
||||||
|
t->index++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise we need to check the rest of the found string
|
||||||
|
int diff = origIndex - t->index;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < t->index; ++i) {
|
||||||
|
if (t->str[i] != t->str[i + diff]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we successfully got through the previous loop then our current
|
||||||
|
// index is good.
|
||||||
|
if (i == t->index) {
|
||||||
|
t->index++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise we just try the next index
|
||||||
|
} while (t->index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// unreachable
|
||||||
|
return -1;
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
Stream.h - base class for character-based streams.
|
||||||
|
Copyright (c) 2010 David A. Mellis. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
parsing functions based on TextFinder library by Michael Margolis
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "Print.h"
|
||||||
|
|
||||||
|
// compatibility macros for testing
|
||||||
|
/*
|
||||||
|
#define getInt() parseInt()
|
||||||
|
#define getInt(ignore) parseInt(ignore)
|
||||||
|
#define getFloat() parseFloat()
|
||||||
|
#define getFloat(ignore) parseFloat(ignore)
|
||||||
|
#define getString(pre_string, post_string, buffer, length)
|
||||||
|
readBytesBetween(pre_string, terminator, buffer, length)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This enumeration provides the lookahead options for parseInt(), parseFloat()
|
||||||
|
// The rules set out here are used until either the first valid character is found
|
||||||
|
// or a time out occurs due to lack of input.
|
||||||
|
enum LookaheadMode {
|
||||||
|
SKIP_ALL, // All invalid characters are ignored.
|
||||||
|
SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid.
|
||||||
|
SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped.
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field
|
||||||
|
|
||||||
|
class Stream : public Print {
|
||||||
|
protected:
|
||||||
|
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
|
||||||
|
//unsigned long _startMillis; // Formerly used for timer measurement. Unnecessary. Removed and assembly dependent on the class structure adjusted.
|
||||||
|
int timedRead(); // private method to read stream with timeout
|
||||||
|
int timedPeek(); // private method to peek stream with timeout
|
||||||
|
int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual int available() = 0;
|
||||||
|
virtual int read() = 0;
|
||||||
|
virtual int peek() = 0;
|
||||||
|
virtual void flush() = 0;
|
||||||
|
|
||||||
|
Stream() {
|
||||||
|
_timeout = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsing methods
|
||||||
|
|
||||||
|
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||||
|
unsigned long getTimeout(void) {
|
||||||
|
return _timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find(char * target); // reads data from the stream until the target string is found
|
||||||
|
bool find(uint8_t * target) {
|
||||||
|
return find((char *) target);
|
||||||
|
}
|
||||||
|
// returns true if target string is found, false if timed out (see setTimeout)
|
||||||
|
|
||||||
|
bool find(char * target, size_t length); // reads data from the stream until the target string of given length is found
|
||||||
|
bool find(uint8_t * target, size_t length) {
|
||||||
|
return find((char *) target, length);
|
||||||
|
}
|
||||||
|
// returns true if target string is found, false if timed out
|
||||||
|
|
||||||
|
bool find(char target) {
|
||||||
|
return find(&target, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findUntil(char * target, char *terminator); // as find but search ends if the terminator string is found
|
||||||
|
bool findUntil(uint8_t * target, char *terminator) {
|
||||||
|
return findUntil((char *) target, terminator);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findUntil(char * target, size_t targetLen, char *terminate, size_t termLen); // as above but search ends if the terminate string is found
|
||||||
|
bool findUntil(uint8_t * target, size_t targetLen, char *terminate, size_t termLen) {
|
||||||
|
return findUntil((char *) target, targetLen, terminate, termLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
long parseInt(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
|
||||||
|
// returns the first valid (long) integer value from the current position.
|
||||||
|
// lookahead determines how parseInt looks ahead in the stream.
|
||||||
|
// See LookaheadMode enumeration at the top of the file.
|
||||||
|
// Lookahead is terminated by the first character that is not a valid part of an integer.
|
||||||
|
// Once parsing commences, 'ignore' will be skipped in the stream.
|
||||||
|
|
||||||
|
float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
|
||||||
|
// float version of parseInt
|
||||||
|
|
||||||
|
size_t readBytes(char * buffer, size_t length); // read chars from stream into buffer
|
||||||
|
size_t readBytes(uint8_t * buffer, size_t length) {
|
||||||
|
return readBytes((char *) buffer, length);
|
||||||
|
}
|
||||||
|
// terminates if length characters have been read or timeout (see setTimeout)
|
||||||
|
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||||
|
|
||||||
|
size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character
|
||||||
|
size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) {
|
||||||
|
return readBytesUntil(terminator, (char *)buffer, length);
|
||||||
|
}
|
||||||
|
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||||
|
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||||
|
|
||||||
|
// Arduino String functions to be added here
|
||||||
|
String readString();
|
||||||
|
String readStringUntil(char terminator);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
long parseInt(char ignore) {
|
||||||
|
return parseInt(SKIP_ALL, ignore);
|
||||||
|
}
|
||||||
|
float parseFloat(char ignore) {
|
||||||
|
return parseFloat(SKIP_ALL, ignore);
|
||||||
|
}
|
||||||
|
// These overload exists for compatibility with any class that has derived
|
||||||
|
// Stream and used parseFloat/Int with a custom ignore character. To keep
|
||||||
|
// the public API simple, these overload remains protected.
|
||||||
|
|
||||||
|
struct MultiTarget {
|
||||||
|
const char * str; // string you're searching for
|
||||||
|
size_t len; // length of string you're searching for
|
||||||
|
size_t index; // index used by the search routine.
|
||||||
|
};
|
||||||
|
|
||||||
|
// This allows you to search for an arbitrary number of strings.
|
||||||
|
// Returns index of the target that is found first or -1 if timeout occurs.
|
||||||
|
int findMulti(struct MultiTarget * targets, int tCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef NO_IGNORE_CHAR
|
|
@ -0,0 +1,815 @@
|
||||||
|
/*
|
||||||
|
String library for Wiring & Arduino
|
||||||
|
...mostly rewritten by Paul Stoffregen...
|
||||||
|
Copyright (c) 2009-10 Hernando Barragan. All rights reserved.
|
||||||
|
Copyright 2011, Paul Stoffregen, paul@pjrc.com
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "String.h"
|
||||||
|
#include "itoa.h"
|
||||||
|
#include "deprecated-avr-comp/avr/dtostrf.h"
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Constructors */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
String::String(const char *cstr) {
|
||||||
|
init();
|
||||||
|
if (cstr) {
|
||||||
|
copy(cstr, strlen(cstr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(const String &value) {
|
||||||
|
init();
|
||||||
|
*this = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(const __FlashStringHelper *pstr) {
|
||||||
|
init();
|
||||||
|
*this = pstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||||
|
String::String(String &&rval) {
|
||||||
|
init();
|
||||||
|
move(rval);
|
||||||
|
}
|
||||||
|
String::String(StringSumHelper &&rval) {
|
||||||
|
init();
|
||||||
|
move(rval);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String::String(char c) {
|
||||||
|
init();
|
||||||
|
char buf[2];
|
||||||
|
buf[0] = c;
|
||||||
|
buf[1] = 0;
|
||||||
|
*this = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(unsigned char value, unsigned char base) {
|
||||||
|
init();
|
||||||
|
char buf[1 + 8 * sizeof(unsigned char)];
|
||||||
|
utoa(value, buf, base);
|
||||||
|
*this = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(int value, unsigned char base) {
|
||||||
|
init();
|
||||||
|
char buf[2 + 8 * sizeof(int)];
|
||||||
|
itoa(value, buf, base);
|
||||||
|
*this = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(unsigned int value, unsigned char base) {
|
||||||
|
init();
|
||||||
|
char buf[1 + 8 * sizeof(unsigned int)];
|
||||||
|
utoa(value, buf, base);
|
||||||
|
*this = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(long value, unsigned char base) {
|
||||||
|
init();
|
||||||
|
char buf[2 + 8 * sizeof(long)];
|
||||||
|
ltoa(value, buf, base);
|
||||||
|
*this = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(unsigned long value, unsigned char base) {
|
||||||
|
init();
|
||||||
|
char buf[1 + 8 * sizeof(unsigned long)];
|
||||||
|
ultoa(value, buf, base);
|
||||||
|
*this = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(float value, unsigned char decimalPlaces) {
|
||||||
|
init();
|
||||||
|
char buf[33];
|
||||||
|
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(double value, unsigned char decimalPlaces) {
|
||||||
|
init();
|
||||||
|
char buf[33];
|
||||||
|
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
String::~String() {
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Memory Management */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
inline void String::init(void) {
|
||||||
|
buffer = NULL;
|
||||||
|
capacity = 0;
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::invalidate(void) {
|
||||||
|
if (buffer) {
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
buffer = NULL;
|
||||||
|
capacity = len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::reserve(unsigned int size) {
|
||||||
|
if (buffer && capacity >= size) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (changeBuffer(size)) {
|
||||||
|
if (len == 0) {
|
||||||
|
buffer[0] = 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::changeBuffer(unsigned int maxStrLen) {
|
||||||
|
char *newbuffer = (char *)realloc(buffer, maxStrLen + 1);
|
||||||
|
if (newbuffer) {
|
||||||
|
buffer = newbuffer;
|
||||||
|
capacity = maxStrLen;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Copy and Move */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
String &String::copy(const char *cstr, unsigned int length) {
|
||||||
|
if (!reserve(length)) {
|
||||||
|
invalidate();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
len = length;
|
||||||
|
strcpy(buffer, cstr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::copy(const __FlashStringHelper *pstr, unsigned int length) {
|
||||||
|
if (!reserve(length)) {
|
||||||
|
invalidate();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
len = length;
|
||||||
|
strcpy_P(buffer, (PGM_P)pstr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||||
|
void String::move(String &rhs) {
|
||||||
|
if (buffer) {
|
||||||
|
if (rhs && capacity >= rhs.len) {
|
||||||
|
strcpy(buffer, rhs.buffer);
|
||||||
|
len = rhs.len;
|
||||||
|
rhs.len = 0;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer = rhs.buffer;
|
||||||
|
capacity = rhs.capacity;
|
||||||
|
len = rhs.len;
|
||||||
|
rhs.buffer = NULL;
|
||||||
|
rhs.capacity = 0;
|
||||||
|
rhs.len = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String &String::operator = (const String &rhs) {
|
||||||
|
if (this == &rhs) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rhs.buffer) {
|
||||||
|
copy(rhs.buffer, rhs.len);
|
||||||
|
} else {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||||
|
String &String::operator = (String &&rval) {
|
||||||
|
if (this != &rval) {
|
||||||
|
move(rval);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::operator = (StringSumHelper &&rval) {
|
||||||
|
if (this != &rval) {
|
||||||
|
move(rval);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String &String::operator = (const char *cstr) {
|
||||||
|
if (cstr) {
|
||||||
|
copy(cstr, strlen(cstr));
|
||||||
|
} else {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::operator = (const __FlashStringHelper *pstr) {
|
||||||
|
if (pstr) {
|
||||||
|
copy(pstr, strlen_P((PGM_P)pstr));
|
||||||
|
} else {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* concat */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
unsigned char String::concat(const String &s) {
|
||||||
|
return concat(s.buffer, s.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(const char *cstr, unsigned int length) {
|
||||||
|
unsigned int newlen = len + length;
|
||||||
|
if (!cstr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (length == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!reserve(newlen)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strcpy(buffer + len, cstr);
|
||||||
|
len = newlen;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(const char *cstr) {
|
||||||
|
if (!cstr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return concat(cstr, strlen(cstr));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(char c) {
|
||||||
|
char buf[2];
|
||||||
|
buf[0] = c;
|
||||||
|
buf[1] = 0;
|
||||||
|
return concat(buf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(unsigned char num) {
|
||||||
|
char buf[1 + 3 * sizeof(unsigned char)];
|
||||||
|
itoa(num, buf, 10);
|
||||||
|
return concat(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(int num) {
|
||||||
|
char buf[2 + 3 * sizeof(int)];
|
||||||
|
itoa(num, buf, 10);
|
||||||
|
return concat(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(unsigned int num) {
|
||||||
|
char buf[1 + 3 * sizeof(unsigned int)];
|
||||||
|
utoa(num, buf, 10);
|
||||||
|
return concat(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(long num) {
|
||||||
|
char buf[2 + 3 * sizeof(long)];
|
||||||
|
ltoa(num, buf, 10);
|
||||||
|
return concat(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(unsigned long num) {
|
||||||
|
char buf[1 + 3 * sizeof(unsigned long)];
|
||||||
|
ultoa(num, buf, 10);
|
||||||
|
return concat(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(float num) {
|
||||||
|
char buf[20];
|
||||||
|
char *string = dtostrf(num, 4, 2, buf);
|
||||||
|
return concat(string, strlen(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(double num) {
|
||||||
|
char buf[20];
|
||||||
|
char *string = dtostrf(num, 4, 2, buf);
|
||||||
|
return concat(string, strlen(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(const __FlashStringHelper *str) {
|
||||||
|
if (!str) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int length = strlen_P((const char *) str);
|
||||||
|
if (length == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
unsigned int newlen = len + length;
|
||||||
|
if (!reserve(newlen)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strcpy_P(buffer + len, (const char *) str);
|
||||||
|
len = newlen;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Concatenate */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, const String &rhs) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(rhs.buffer, rhs.len)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, const char *cstr) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!cstr || !a.concat(cstr, strlen(cstr))) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, char c) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(c)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, unsigned char num) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(num)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, int num) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(num)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, unsigned int num) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(num)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, long num) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(num)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, unsigned long num) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(num)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, float num) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(num)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, double num) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(num)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper &operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs) {
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||||
|
if (!a.concat(rhs)) {
|
||||||
|
a.invalidate();
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Comparison */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
int String::compareTo(const String &s) const {
|
||||||
|
if (!buffer || !s.buffer) {
|
||||||
|
if (s.buffer && s.len > 0) {
|
||||||
|
return 0 - *(unsigned char *)s.buffer;
|
||||||
|
}
|
||||||
|
if (buffer && len > 0) {
|
||||||
|
return *(unsigned char *)buffer;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return strcmp(buffer, s.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::equals(const String &s2) const {
|
||||||
|
return (len == s2.len && compareTo(s2) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::equals(const char *cstr) const {
|
||||||
|
if (len == 0) {
|
||||||
|
return (cstr == NULL || *cstr == 0);
|
||||||
|
}
|
||||||
|
if (cstr == NULL) {
|
||||||
|
return buffer[0] == 0;
|
||||||
|
}
|
||||||
|
return strcmp(buffer, cstr) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::operator<(const String &rhs) const {
|
||||||
|
return compareTo(rhs) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::operator>(const String &rhs) const {
|
||||||
|
return compareTo(rhs) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::operator<=(const String &rhs) const {
|
||||||
|
return compareTo(rhs) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::operator>=(const String &rhs) const {
|
||||||
|
return compareTo(rhs) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::equalsIgnoreCase(const String &s2) const {
|
||||||
|
if (this == &s2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (len != s2.len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const char *p1 = buffer;
|
||||||
|
const char *p2 = s2.buffer;
|
||||||
|
while (*p1) {
|
||||||
|
if (tolower(*p1++) != tolower(*p2++)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::startsWith(const String &s2) const {
|
||||||
|
if (len < s2.len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return startsWith(s2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::startsWith(const String &s2, unsigned int offset) const {
|
||||||
|
if (offset > len - s2.len || !buffer || !s2.buffer) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return strncmp(&buffer[offset], s2.buffer, s2.len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::endsWith(const String &s2) const {
|
||||||
|
if (len < s2.len || !buffer || !s2.buffer) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return strcmp(&buffer[len - s2.len], s2.buffer) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Character Access */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
char String::charAt(unsigned int loc) const {
|
||||||
|
return operator[](loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::setCharAt(unsigned int loc, char c) {
|
||||||
|
if (loc < len) {
|
||||||
|
buffer[loc] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char &String::operator[](unsigned int index) {
|
||||||
|
static char dummy_writable_char;
|
||||||
|
if (index >= len || !buffer) {
|
||||||
|
dummy_writable_char = 0;
|
||||||
|
return dummy_writable_char;
|
||||||
|
}
|
||||||
|
return buffer[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
char String::operator[](unsigned int index) const {
|
||||||
|
if (index >= len || !buffer) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return buffer[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const {
|
||||||
|
if (!bufsize || !buf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (index >= len) {
|
||||||
|
buf[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned int n = bufsize - 1;
|
||||||
|
if (n > len - index) {
|
||||||
|
n = len - index;
|
||||||
|
}
|
||||||
|
strncpy((char *)buf, buffer + index, n);
|
||||||
|
buf[n] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Search */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
int String::indexOf(char c) const {
|
||||||
|
return indexOf(c, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::indexOf(char ch, unsigned int fromIndex) const {
|
||||||
|
if (fromIndex >= len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const char *temp = strchr(buffer + fromIndex, ch);
|
||||||
|
if (temp == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return temp - buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::indexOf(const String &s2) const {
|
||||||
|
return indexOf(s2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::indexOf(const String &s2, unsigned int fromIndex) const {
|
||||||
|
if (fromIndex >= len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const char *found = strstr(buffer + fromIndex, s2.buffer);
|
||||||
|
if (found == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return found - buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::lastIndexOf(char theChar) const {
|
||||||
|
return lastIndexOf(theChar, len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::lastIndexOf(char ch, unsigned int fromIndex) const {
|
||||||
|
if (fromIndex >= len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
char tempchar = buffer[fromIndex + 1];
|
||||||
|
buffer[fromIndex + 1] = '\0';
|
||||||
|
char *temp = strrchr(buffer, ch);
|
||||||
|
buffer[fromIndex + 1] = tempchar;
|
||||||
|
if (temp == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return temp - buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::lastIndexOf(const String &s2) const {
|
||||||
|
return lastIndexOf(s2, len - s2.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::lastIndexOf(const String &s2, unsigned int fromIndex) const {
|
||||||
|
if (s2.len == 0 || len == 0 || s2.len > len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fromIndex >= len) {
|
||||||
|
fromIndex = len - 1;
|
||||||
|
}
|
||||||
|
int found = -1;
|
||||||
|
for (char *p = buffer; p <= buffer + fromIndex; p++) {
|
||||||
|
p = strstr(p, s2.buffer);
|
||||||
|
if (!p) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((unsigned int)(p - buffer) <= fromIndex) {
|
||||||
|
found = p - buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
String String::substring(unsigned int left, unsigned int right) const {
|
||||||
|
if (left > right) {
|
||||||
|
unsigned int temp = right;
|
||||||
|
right = left;
|
||||||
|
left = temp;
|
||||||
|
}
|
||||||
|
String out;
|
||||||
|
if (left >= len) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
if (right > len) {
|
||||||
|
right = len;
|
||||||
|
}
|
||||||
|
char temp = buffer[right]; // save the replaced character
|
||||||
|
buffer[right] = '\0';
|
||||||
|
out = buffer + left; // pointer arithmetic
|
||||||
|
buffer[right] = temp; // restore character
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Modification */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
void String::replace(char find, char replace) {
|
||||||
|
if (!buffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (char *p = buffer; *p; p++) {
|
||||||
|
if (*p == find) {
|
||||||
|
*p = replace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::replace(const String &find, const String &replace) {
|
||||||
|
if (len == 0 || find.len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int diff = replace.len - find.len;
|
||||||
|
char *readFrom = buffer;
|
||||||
|
char *foundAt;
|
||||||
|
if (diff == 0) {
|
||||||
|
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
|
||||||
|
memcpy(foundAt, replace.buffer, replace.len);
|
||||||
|
readFrom = foundAt + replace.len;
|
||||||
|
}
|
||||||
|
} else if (diff < 0) {
|
||||||
|
char *writeTo = buffer;
|
||||||
|
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
|
||||||
|
unsigned int n = foundAt - readFrom;
|
||||||
|
memcpy(writeTo, readFrom, n);
|
||||||
|
writeTo += n;
|
||||||
|
memcpy(writeTo, replace.buffer, replace.len);
|
||||||
|
writeTo += replace.len;
|
||||||
|
readFrom = foundAt + find.len;
|
||||||
|
len += diff;
|
||||||
|
}
|
||||||
|
strcpy(writeTo, readFrom);
|
||||||
|
} else {
|
||||||
|
unsigned int size = len; // compute size needed for result
|
||||||
|
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
|
||||||
|
readFrom = foundAt + find.len;
|
||||||
|
size += diff;
|
||||||
|
}
|
||||||
|
if (size == len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (size > capacity && !changeBuffer(size)) {
|
||||||
|
return; // XXX: tell user!
|
||||||
|
}
|
||||||
|
int index = len - 1;
|
||||||
|
while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) {
|
||||||
|
readFrom = buffer + index + find.len;
|
||||||
|
memmove(readFrom + diff, readFrom, len - (readFrom - buffer));
|
||||||
|
len += diff;
|
||||||
|
buffer[len] = 0;
|
||||||
|
memcpy(buffer + index, replace.buffer, replace.len);
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::remove(unsigned int index) {
|
||||||
|
// Pass the biggest integer as the count. The remove method
|
||||||
|
// below will take care of truncating it at the end of the
|
||||||
|
// string.
|
||||||
|
remove(index, (unsigned int) -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::remove(unsigned int index, unsigned int count) {
|
||||||
|
if (index >= len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (count <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (count > len - index) {
|
||||||
|
count = len - index;
|
||||||
|
}
|
||||||
|
char *writeTo = buffer + index;
|
||||||
|
len = len - count;
|
||||||
|
strncpy(writeTo, buffer + index + count, len - index);
|
||||||
|
buffer[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::toLowerCase(void) {
|
||||||
|
if (!buffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (char *p = buffer; *p; p++) {
|
||||||
|
*p = tolower(*p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::toUpperCase(void) {
|
||||||
|
if (!buffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (char *p = buffer; *p; p++) {
|
||||||
|
*p = toupper(*p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::trim(void) {
|
||||||
|
if (!buffer || len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *begin = buffer;
|
||||||
|
while (isspace(*begin)) {
|
||||||
|
begin++;
|
||||||
|
}
|
||||||
|
char *end = buffer + len - 1;
|
||||||
|
while (isspace(*end) && end >= begin) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
len = end + 1 - begin;
|
||||||
|
if (begin > buffer) {
|
||||||
|
memcpy(buffer, begin, len);
|
||||||
|
}
|
||||||
|
buffer[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Parsing / Conversion */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
long String::toInt(void) const {
|
||||||
|
if (buffer) {
|
||||||
|
return atol(buffer);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float String::toFloat(void) const {
|
||||||
|
return float(toDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
double String::toDouble(void) const {
|
||||||
|
if (buffer) {
|
||||||
|
return atof(buffer);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
String library for Wiring & Arduino
|
||||||
|
...mostly rewritten by Paul Stoffregen...
|
||||||
|
Copyright (c) 2009-10 Hernando Barragan. All right reserved.
|
||||||
|
Copyright 2011, Paul Stoffregen, paul@pjrc.com
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#if defined(__AVR__)
|
||||||
|
#include "avr/pgmspace.h"
|
||||||
|
#else
|
||||||
|
#include "deprecated-avr-comp/avr/pgmspace.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// When compiling programs with this class, the following gcc parameters
|
||||||
|
// dramatically increase performance and memory (RAM) efficiency, typically
|
||||||
|
// with little or no increase in code size.
|
||||||
|
// -felide-constructors
|
||||||
|
// -std=c++0x
|
||||||
|
|
||||||
|
class __FlashStringHelper;
|
||||||
|
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// An inherited class for holding the result of a concatenation. These
|
||||||
|
// result objects are assumed to be writable by subsequent concatenations.
|
||||||
|
class StringSumHelper;
|
||||||
|
|
||||||
|
// The string class
|
||||||
|
class String {
|
||||||
|
// use a function pointer to allow for "if (s)" without the
|
||||||
|
// complications of an operator bool(). for more information, see:
|
||||||
|
// http://www.artima.com/cppsource/safebool.html
|
||||||
|
typedef void (String::*StringIfHelperType)() const;
|
||||||
|
void StringIfHelper() const {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// constructors
|
||||||
|
// creates a copy of the initial value.
|
||||||
|
// if the initial value is null or invalid, or if memory allocation
|
||||||
|
// fails, the string will be marked as invalid (i.e. "if (s)" will
|
||||||
|
// be false).
|
||||||
|
String(const char *cstr = "");
|
||||||
|
String(const String &str);
|
||||||
|
String(const __FlashStringHelper *str);
|
||||||
|
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||||
|
String(String &&rval);
|
||||||
|
String(StringSumHelper &&rval);
|
||||||
|
#endif
|
||||||
|
explicit String(char c);
|
||||||
|
explicit String(unsigned char, unsigned char base = 10);
|
||||||
|
explicit String(int, unsigned char base = 10);
|
||||||
|
explicit String(unsigned int, unsigned char base = 10);
|
||||||
|
explicit String(long, unsigned char base = 10);
|
||||||
|
explicit String(unsigned long, unsigned char base = 10);
|
||||||
|
explicit String(float, unsigned char decimalPlaces = 2);
|
||||||
|
explicit String(double, unsigned char decimalPlaces = 2);
|
||||||
|
~String(void);
|
||||||
|
|
||||||
|
// memory management
|
||||||
|
// return true on success, false on failure (in which case, the string
|
||||||
|
// is left unchanged). reserve(0), if successful, will validate an
|
||||||
|
// invalid string (i.e., "if (s)" will be true afterwards)
|
||||||
|
unsigned char reserve(unsigned int size);
|
||||||
|
inline unsigned int length(void) const {
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates a copy of the assigned value. if the value is null or
|
||||||
|
// invalid, or if the memory allocation fails, the string will be
|
||||||
|
// marked as invalid ("if (s)" will be false).
|
||||||
|
String &operator = (const String &rhs);
|
||||||
|
String &operator = (const char *cstr);
|
||||||
|
String &operator = (const __FlashStringHelper *str);
|
||||||
|
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||||
|
String &operator = (String &&rval);
|
||||||
|
String &operator = (StringSumHelper &&rval);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// concatenate (works w/ built-in types)
|
||||||
|
|
||||||
|
// returns true on success, false on failure (in which case, the string
|
||||||
|
// is left unchanged). if the argument is null or invalid, the
|
||||||
|
// concatenation is considered unsuccessful.
|
||||||
|
unsigned char concat(const String &str);
|
||||||
|
unsigned char concat(const char *cstr);
|
||||||
|
unsigned char concat(char c);
|
||||||
|
unsigned char concat(unsigned char c);
|
||||||
|
unsigned char concat(int num);
|
||||||
|
unsigned char concat(unsigned int num);
|
||||||
|
unsigned char concat(long num);
|
||||||
|
unsigned char concat(unsigned long num);
|
||||||
|
unsigned char concat(float num);
|
||||||
|
unsigned char concat(double num);
|
||||||
|
unsigned char concat(const __FlashStringHelper *str);
|
||||||
|
|
||||||
|
// if there's not enough memory for the concatenated value, the string
|
||||||
|
// will be left unchanged (but this isn't signalled in any way)
|
||||||
|
String &operator += (const String &rhs) {
|
||||||
|
concat(rhs);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (const char *cstr) {
|
||||||
|
concat(cstr);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (char c) {
|
||||||
|
concat(c);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (unsigned char num) {
|
||||||
|
concat(num);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (int num) {
|
||||||
|
concat(num);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (unsigned int num) {
|
||||||
|
concat(num);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (long num) {
|
||||||
|
concat(num);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (unsigned long num) {
|
||||||
|
concat(num);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (float num) {
|
||||||
|
concat(num);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (double num) {
|
||||||
|
concat(num);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
String &operator += (const __FlashStringHelper *str) {
|
||||||
|
concat(str);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, const String &rhs);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, const char *cstr);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, char c);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, unsigned char num);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, int num);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, unsigned int num);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, long num);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, unsigned long num);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, float num);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, double num);
|
||||||
|
friend StringSumHelper &operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs);
|
||||||
|
|
||||||
|
// comparison (only works w/ Strings and "strings")
|
||||||
|
operator StringIfHelperType() const {
|
||||||
|
return buffer ? &String::StringIfHelper : 0;
|
||||||
|
}
|
||||||
|
int compareTo(const String &s) const;
|
||||||
|
unsigned char equals(const String &s) const;
|
||||||
|
unsigned char equals(const char *cstr) const;
|
||||||
|
unsigned char operator == (const String &rhs) const {
|
||||||
|
return equals(rhs);
|
||||||
|
}
|
||||||
|
unsigned char operator == (const char *cstr) const {
|
||||||
|
return equals(cstr);
|
||||||
|
}
|
||||||
|
unsigned char operator != (const String &rhs) const {
|
||||||
|
return !equals(rhs);
|
||||||
|
}
|
||||||
|
unsigned char operator != (const char *cstr) const {
|
||||||
|
return !equals(cstr);
|
||||||
|
}
|
||||||
|
unsigned char operator < (const String &rhs) const;
|
||||||
|
unsigned char operator > (const String &rhs) const;
|
||||||
|
unsigned char operator <= (const String &rhs) const;
|
||||||
|
unsigned char operator >= (const String &rhs) const;
|
||||||
|
unsigned char equalsIgnoreCase(const String &s) const;
|
||||||
|
unsigned char startsWith(const String &prefix) const;
|
||||||
|
unsigned char startsWith(const String &prefix, unsigned int offset) const;
|
||||||
|
unsigned char endsWith(const String &suffix) const;
|
||||||
|
|
||||||
|
// character access
|
||||||
|
char charAt(unsigned int index) const;
|
||||||
|
void setCharAt(unsigned int index, char c);
|
||||||
|
char operator [](unsigned int index) const;
|
||||||
|
char &operator [](unsigned int index);
|
||||||
|
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const;
|
||||||
|
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
|
||||||
|
getBytes((unsigned char *)buf, bufsize, index);
|
||||||
|
}
|
||||||
|
const char *c_str() const {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
char *begin() {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
char *end() {
|
||||||
|
return buffer + length();
|
||||||
|
}
|
||||||
|
const char *begin() const {
|
||||||
|
return c_str();
|
||||||
|
}
|
||||||
|
const char *end() const {
|
||||||
|
return c_str() + length();
|
||||||
|
}
|
||||||
|
|
||||||
|
// search
|
||||||
|
int indexOf(char ch) const;
|
||||||
|
int indexOf(char ch, unsigned int fromIndex) const;
|
||||||
|
int indexOf(const String &str) const;
|
||||||
|
int indexOf(const String &str, unsigned int fromIndex) const;
|
||||||
|
int lastIndexOf(char ch) const;
|
||||||
|
int lastIndexOf(char ch, unsigned int fromIndex) const;
|
||||||
|
int lastIndexOf(const String &str) const;
|
||||||
|
int lastIndexOf(const String &str, unsigned int fromIndex) const;
|
||||||
|
String substring(unsigned int beginIndex) const {
|
||||||
|
return substring(beginIndex, len);
|
||||||
|
};
|
||||||
|
String substring(unsigned int beginIndex, unsigned int endIndex) const;
|
||||||
|
|
||||||
|
// modification
|
||||||
|
void replace(char find, char replace);
|
||||||
|
void replace(const String &find, const String &replace);
|
||||||
|
void remove(unsigned int index);
|
||||||
|
void remove(unsigned int index, unsigned int count);
|
||||||
|
void toLowerCase(void);
|
||||||
|
void toUpperCase(void);
|
||||||
|
void trim(void);
|
||||||
|
|
||||||
|
// parsing/conversion
|
||||||
|
long toInt(void) const;
|
||||||
|
float toFloat(void) const;
|
||||||
|
double toDouble(void) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
char *buffer; // the actual char array
|
||||||
|
unsigned int capacity; // the array length minus one (for the '\0')
|
||||||
|
unsigned int len; // the String length (not counting the '\0')
|
||||||
|
protected:
|
||||||
|
void init(void);
|
||||||
|
void invalidate(void);
|
||||||
|
unsigned char changeBuffer(unsigned int maxStrLen);
|
||||||
|
unsigned char concat(const char *cstr, unsigned int length);
|
||||||
|
|
||||||
|
// copy and move
|
||||||
|
String ©(const char *cstr, unsigned int length);
|
||||||
|
String ©(const __FlashStringHelper *pstr, unsigned int length);
|
||||||
|
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||||
|
void move(String &rhs);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class StringSumHelper : public String {
|
||||||
|
public:
|
||||||
|
StringSumHelper(const String &s) : String(s) {}
|
||||||
|
StringSumHelper(const char *p) : String(p) {}
|
||||||
|
StringSumHelper(char c) : String(c) {}
|
||||||
|
StringSumHelper(unsigned char num) : String(num) {}
|
||||||
|
StringSumHelper(int num) : String(num) {}
|
||||||
|
StringSumHelper(unsigned int num) : String(num) {}
|
||||||
|
StringSumHelper(long num) : String(num) {}
|
||||||
|
StringSumHelper(unsigned long num) : String(num) {}
|
||||||
|
StringSumHelper(float num) : String(num) {}
|
||||||
|
StringSumHelper(double num) : String(num) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
USBAPI.h
|
||||||
|
Copyright (c) 2005-2014 Arduino. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __USBAPI__
|
||||||
|
#define __USBAPI__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Low level API
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
uint8_t bmRequestType;
|
||||||
|
struct {
|
||||||
|
uint8_t direction : 5;
|
||||||
|
uint8_t type : 2;
|
||||||
|
uint8_t transferDirection : 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
uint8_t bRequest;
|
||||||
|
uint8_t wValueL;
|
||||||
|
uint8_t wValueH;
|
||||||
|
uint16_t wIndex;
|
||||||
|
uint16_t wLength;
|
||||||
|
} USBSetup;
|
||||||
|
|
||||||
|
|
||||||
|
int USB_SendControl(uint8_t flags, const void* d, int len);
|
||||||
|
int USB_RecvControl(void* d, int len);
|
||||||
|
int USB_RecvControlLong(void* d, int len);
|
||||||
|
|
||||||
|
uint8_t USB_Available(uint8_t ep);
|
||||||
|
uint8_t USB_SendSpace(uint8_t ep);
|
||||||
|
int USB_Send(uint8_t ep, const void* data, int len); // blocking
|
||||||
|
int USB_Recv(uint8_t ep, void* data, int len); // non-blocking
|
||||||
|
int USB_Recv(uint8_t ep); // non-blocking
|
||||||
|
void USB_Flush(uint8_t ep);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Udp.cpp: Library to send/receive UDP packets.
|
||||||
|
*
|
||||||
|
* NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
|
||||||
|
* 1) UDP does not guarantee the order in which assembled UDP packets are received. This
|
||||||
|
* might not happen often in practice, but in larger network topologies, a UDP
|
||||||
|
* packet can be received out of sequence.
|
||||||
|
* 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being
|
||||||
|
* aware of it. Again, this may not be a concern in practice on small local networks.
|
||||||
|
* For more information, see http://www.cafeaulait.org/course/week12/35.html
|
||||||
|
*
|
||||||
|
* MIT License:
|
||||||
|
* Copyright (c) 2008 Bjoern Hartmann
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* bjoern@cs.stanford.edu 12/30/2008
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Stream.h"
|
||||||
|
#include "IPAddress.h"
|
||||||
|
|
||||||
|
class UDP : public Stream {
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual uint8_t begin(uint16_t) = 0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
|
||||||
|
virtual void stop() = 0; // Finish with the UDP socket
|
||||||
|
|
||||||
|
// Sending UDP packets
|
||||||
|
|
||||||
|
// Start building up a packet to send to the remote host specific in ip and port
|
||||||
|
// Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
|
||||||
|
virtual int beginPacket(IPAddress ip, uint16_t port) = 0;
|
||||||
|
// Start building up a packet to send to the remote host specific in host and port
|
||||||
|
// Returns 1 if successful, 0 if there was a problem resolving the hostname or port
|
||||||
|
virtual int beginPacket(const char *host, uint16_t port) = 0;
|
||||||
|
// Finish off this packet and send it
|
||||||
|
// Returns 1 if the packet was sent successfully, 0 if there was an error
|
||||||
|
virtual int endPacket() = 0;
|
||||||
|
// Write a single byte into the packet
|
||||||
|
virtual size_t write(uint8_t) = 0;
|
||||||
|
// Write size bytes from buffer into the packet
|
||||||
|
virtual size_t write(const uint8_t *buffer, size_t size) = 0;
|
||||||
|
|
||||||
|
// Start processing the next available incoming packet
|
||||||
|
// Returns the size of the packet in bytes, or 0 if no packets are available
|
||||||
|
virtual int parsePacket() = 0;
|
||||||
|
// Number of bytes remaining in the current packet
|
||||||
|
virtual int available() = 0;
|
||||||
|
// Read a single byte from the current packet
|
||||||
|
virtual int read() = 0;
|
||||||
|
// Read up to len bytes from the current packet and place them into buffer
|
||||||
|
// Returns the number of bytes read, or 0 if none are available
|
||||||
|
virtual int read(unsigned char *buffer, size_t len) = 0;
|
||||||
|
// Read up to len characters from the current packet and place them into buffer
|
||||||
|
// Returns the number of characters read, or 0 if none are available
|
||||||
|
virtual int read(char *buffer, size_t len) = 0;
|
||||||
|
// Return the next byte from the current packet without moving on to the next byte
|
||||||
|
virtual int peek() = 0;
|
||||||
|
virtual void flush() = 0; // Finish reading the current packet
|
||||||
|
|
||||||
|
// Return the IP address of the host who sent the current incoming packet
|
||||||
|
virtual IPAddress remoteIP() = 0;
|
||||||
|
// Return the port of the host who sent the current incoming packet
|
||||||
|
virtual uint16_t remotePort() = 0;
|
||||||
|
protected:
|
||||||
|
uint8_t *rawIPAddress(IPAddress &addr) {
|
||||||
|
return addr.raw_address();
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
WCharacter.h - Character utility functions for Wiring & Arduino
|
||||||
|
Copyright (c) 2010 Hernando Barragan. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Character_h
|
||||||
|
#define Character_h
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
// WCharacter.h prototypes
|
||||||
|
inline bool isAlphaNumeric(int c) __attribute__((always_inline));
|
||||||
|
inline bool isAlpha(int c) __attribute__((always_inline));
|
||||||
|
inline bool isAscii(int c) __attribute__((always_inline));
|
||||||
|
inline bool isWhitespace(int c) __attribute__((always_inline));
|
||||||
|
inline bool isControl(int c) __attribute__((always_inline));
|
||||||
|
inline bool isDigit(int c) __attribute__((always_inline));
|
||||||
|
inline bool isGraph(int c) __attribute__((always_inline));
|
||||||
|
inline bool isLowerCase(int c) __attribute__((always_inline));
|
||||||
|
inline bool isPrintable(int c) __attribute__((always_inline));
|
||||||
|
inline bool isPunct(int c) __attribute__((always_inline));
|
||||||
|
inline bool isSpace(int c) __attribute__((always_inline));
|
||||||
|
inline bool isUpperCase(int c) __attribute__((always_inline));
|
||||||
|
inline bool isHexadecimalDigit(int c) __attribute__((always_inline));
|
||||||
|
inline int toAscii(int c) __attribute__((always_inline));
|
||||||
|
inline int toLowerCase(int c) __attribute__((always_inline));
|
||||||
|
inline int toUpperCase(int c)__attribute__((always_inline));
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for an alphanumeric character.
|
||||||
|
// It is equivalent to (isalpha(c) || isdigit(c)).
|
||||||
|
inline bool isAlphaNumeric(int c) {
|
||||||
|
return (isalnum(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for an alphabetic character.
|
||||||
|
// It is equivalent to (isupper(c) || islower(c)).
|
||||||
|
inline bool isAlpha(int c) {
|
||||||
|
return (isalpha(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks whether c is a 7-bit unsigned char value
|
||||||
|
// that fits into the ASCII character set.
|
||||||
|
inline bool isAscii(int c) {
|
||||||
|
return (isascii(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for a blank character, that is, a space or a tab.
|
||||||
|
inline bool isWhitespace(int c) {
|
||||||
|
return (isblank(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for a control character.
|
||||||
|
inline bool isControl(int c) {
|
||||||
|
return (iscntrl(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for a digit (0 through 9).
|
||||||
|
inline bool isDigit(int c) {
|
||||||
|
return (isdigit(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for any printable character except space.
|
||||||
|
inline bool isGraph(int c) {
|
||||||
|
return (isgraph(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for a lower-case character.
|
||||||
|
inline bool isLowerCase(int c) {
|
||||||
|
return (islower(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for any printable character including space.
|
||||||
|
inline bool isPrintable(int c) {
|
||||||
|
return (isprint(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for any printable character which is not a space
|
||||||
|
// or an alphanumeric character.
|
||||||
|
inline bool isPunct(int c) {
|
||||||
|
return (ispunct(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for white-space characters. For the avr-libc library,
|
||||||
|
// these are: space, formfeed ('\f'), newline ('\n'), carriage
|
||||||
|
// return ('\r'), horizontal tab ('\t'), and vertical tab ('\v').
|
||||||
|
inline bool isSpace(int c) {
|
||||||
|
return (isspace(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for an uppercase letter.
|
||||||
|
inline bool isUpperCase(int c) {
|
||||||
|
return (isupper(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks for a hexadecimal digits, i.e. one of 0 1 2 3 4 5 6 7
|
||||||
|
// 8 9 a b c d e f A B C D E F.
|
||||||
|
inline bool isHexadecimalDigit(int c) {
|
||||||
|
return (isxdigit(c) == 0 ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Converts c to a 7-bit unsigned char value that fits into the
|
||||||
|
// ASCII character set, by clearing the high-order bits.
|
||||||
|
inline int toAscii(int c) {
|
||||||
|
return toascii(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Warning:
|
||||||
|
// Many people will be unhappy if you use this function.
|
||||||
|
// This function will convert accented letters into random
|
||||||
|
// characters.
|
||||||
|
|
||||||
|
// Converts the letter c to lower case, if possible.
|
||||||
|
inline int toLowerCase(int c) {
|
||||||
|
return tolower(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Converts the letter c to upper case, if possible.
|
||||||
|
inline int toUpperCase(int c) {
|
||||||
|
return toupper(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
dtostrf - Emulation for dtostrf function from avr-libc
|
||||||
|
Copyright (c) 2016 Arduino LLC. All rights reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is a default implementation for dtostrf function.
|
||||||
|
// This file should be used if the standard lib doesn't provide an
|
||||||
|
// implementation of dtostrf.
|
||||||
|
|
||||||
|
// Create a file called "dtostrf.c" with the following include:
|
||||||
|
// #include "api/deprecated-avr-comp/avr/dtostrf.c.impl"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
|
||||||
|
asm(".global _printf_float");
|
||||||
|
|
||||||
|
char fmt[20];
|
||||||
|
sprintf(fmt, "%%%d.%df", width, prec);
|
||||||
|
sprintf(sout, fmt, val);
|
||||||
|
return sout;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
dtostrf - Emulation for dtostrf function from avr-libc
|
||||||
|
Copyright (c) 2015 Arduino LLC. All rights reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(ARDUINO_ARCH_AVR)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *dtostrf(double val, signed char width, unsigned char prec, char *sout);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 Arduino LCC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Empty file.
|
||||||
|
This file is here to allow compatibility with sketches (made for AVR)
|
||||||
|
that includes <AVR/interrupt.h>
|
||||||
|
*/
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
pgmspace.h - Definitions for compatibility with AVR pgmspace macros
|
||||||
|
|
||||||
|
Copyright (c) 2015 Arduino LLC
|
||||||
|
|
||||||
|
Based on work of Paul Stoffregen on Teensy 3 (http://pjrc.com)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PGMSPACE_H_
|
||||||
|
#define __PGMSPACE_H_ 1
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#define PROGMEM
|
||||||
|
#define PGM_P const char *
|
||||||
|
#define PSTR(str) (str)
|
||||||
|
|
||||||
|
#define _SFR_BYTE(n) (n)
|
||||||
|
|
||||||
|
typedef void prog_void;
|
||||||
|
typedef char prog_char;
|
||||||
|
typedef unsigned char prog_uchar;
|
||||||
|
typedef int8_t prog_int8_t;
|
||||||
|
typedef uint8_t prog_uint8_t;
|
||||||
|
typedef int16_t prog_int16_t;
|
||||||
|
typedef uint16_t prog_uint16_t;
|
||||||
|
typedef int32_t prog_int32_t;
|
||||||
|
typedef uint32_t prog_uint32_t;
|
||||||
|
typedef int64_t prog_int64_t;
|
||||||
|
typedef uint64_t prog_uint64_t;
|
||||||
|
|
||||||
|
typedef const void *int_farptr_t;
|
||||||
|
typedef const void *uint_farptr_t;
|
||||||
|
|
||||||
|
#define memchr_P(s, c, n) memchr((s), (c), (n))
|
||||||
|
#define memcmp_P(s1, s2, n) memcmp((s1), (s2), (n))
|
||||||
|
#define memccpy_P(dest, src, c, n) memccpy((dest), (src), (c), (n))
|
||||||
|
#define memcpy_P(dest, src, n) memcpy((dest), (src), (n))
|
||||||
|
#define memmem_P(haystack, haystacklen, needle, needlelen) memmem((haystack), (haystacklen), (needle), (needlelen))
|
||||||
|
#define memrchr_P(s, c, n) memrchr((s), (c), (n))
|
||||||
|
#define strcat_P(dest, src) strcat((dest), (src))
|
||||||
|
#define strchr_P(s, c) strchr((s), (c))
|
||||||
|
#define strchrnul_P(s, c) strchrnul((s), (c))
|
||||||
|
#define strcmp_P(a, b) strcmp((a), (b))
|
||||||
|
#define strcpy_P(dest, src) strcpy((dest), (src))
|
||||||
|
#define strcasecmp_P(s1, s2) strcasecmp((s1), (s2))
|
||||||
|
#define strcasestr_P(haystack, needle) strcasestr((haystack), (needle))
|
||||||
|
#define strcspn_P(s, accept) strcspn((s), (accept))
|
||||||
|
#define strlcat_P(s1, s2, n) strlcat((s1), (s2), (n))
|
||||||
|
#define strlcpy_P(s1, s2, n) strlcpy((s1), (s2), (n))
|
||||||
|
#define strlen_P(a) strlen((a))
|
||||||
|
#define strnlen_P(s, n) strnlen((s), (n))
|
||||||
|
#define strncmp_P(s1, s2, n) strncmp((s1), (s2), (n))
|
||||||
|
#define strncasecmp_P(s1, s2, n) strncasecmp((s1), (s2), (n))
|
||||||
|
#define strncat_P(s1, s2, n) strncat((s1), (s2), (n))
|
||||||
|
#define strncpy_P(s1, s2, n) strncpy((s1), (s2), (n))
|
||||||
|
#define strpbrk_P(s, accept) strpbrk((s), (accept))
|
||||||
|
#define strrchr_P(s, c) strrchr((s), (c))
|
||||||
|
#define strsep_P(sp, delim) strsep((sp), (delim))
|
||||||
|
#define strspn_P(s, accept) strspn((s), (accept))
|
||||||
|
#define strstr_P(a, b) strstr((a), (b))
|
||||||
|
#define strtok_P(s, delim) strtok((s), (delim))
|
||||||
|
#define strtok_rP(s, delim, last) strtok((s), (delim), (last))
|
||||||
|
|
||||||
|
#define strlen_PF(a) strlen((a))
|
||||||
|
#define strnlen_PF(src, len) strnlen((src), (len))
|
||||||
|
#define memcpy_PF(dest, src, len) memcpy((dest), (src), (len))
|
||||||
|
#define strcpy_PF(dest, src) strcpy((dest), (src))
|
||||||
|
#define strncpy_PF(dest, src, len) strncpy((dest), (src), (len))
|
||||||
|
#define strcat_PF(dest, src) strcat((dest), (src))
|
||||||
|
#define strlcat_PF(dest, src, len) strlcat((dest), (src), (len))
|
||||||
|
#define strncat_PF(dest, src, len) strncat((dest), (src), (len))
|
||||||
|
#define strcmp_PF(s1, s2) strcmp((s1), (s2))
|
||||||
|
#define strncmp_PF(s1, s2, n) strncmp((s1), (s2), (n))
|
||||||
|
#define strcasecmp_PF(s1, s2) strcasecmp((s1), (s2))
|
||||||
|
#define strncasecmp_PF(s1, s2, n) strncasecmp((s1), (s2), (n))
|
||||||
|
#define strstr_PF(s1, s2) strstr((s1), (s2))
|
||||||
|
#define strlcpy_PF(dest, src, n) strlcpy((dest), (src), (n))
|
||||||
|
#define memcmp_PF(s1, s2, n) memcmp((s1), (s2), (n))
|
||||||
|
|
||||||
|
#define sprintf_P(s, f, ...) sprintf((s), (f), __VA_ARGS__)
|
||||||
|
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
|
||||||
|
|
||||||
|
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
|
||||||
|
#define pgm_read_word(addr) (*(const unsigned short *)(addr))
|
||||||
|
#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
|
||||||
|
#define pgm_read_float(addr) (*(const float *)(addr))
|
||||||
|
#define pgm_read_ptr(addr) (*(const void *)(addr))
|
||||||
|
|
||||||
|
#define pgm_read_byte_near(addr) pgm_read_byte(addr)
|
||||||
|
#define pgm_read_word_near(addr) pgm_read_word(addr)
|
||||||
|
#define pgm_read_dword_near(addr) pgm_read_dword(addr)
|
||||||
|
#define pgm_read_float_near(addr) pgm_read_float(addr)
|
||||||
|
#define pgm_read_ptr_near(addr) pgm_read_ptr(addr)
|
||||||
|
|
||||||
|
#define pgm_read_byte_far(addr) pgm_read_byte(addr)
|
||||||
|
#define pgm_read_word_far(addr) pgm_read_word(addr)
|
||||||
|
#define pgm_read_dword_far(addr) pgm_read_dword(addr)
|
||||||
|
#define pgm_read_float_far(addr) pgm_read_float(addr)
|
||||||
|
#define pgm_read_ptr_far(addr) pgm_read_ptr(addr)
|
||||||
|
|
||||||
|
#define pgm_get_far_address(addr) (&(addr))
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// including Client.h is deprecated, for all future projects use Arduino.h instead
|
||||||
|
|
||||||
|
// This include is added for compatibility, it will be remove on the next
|
||||||
|
// major release of the API
|
||||||
|
#include "../Client.h"
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// including IPAddress.h is deprecated, for all future projects use Arduino.h instead
|
||||||
|
|
||||||
|
// This include is added for compatibility, it will be remove on the next
|
||||||
|
// major release of the API
|
||||||
|
#include "../IPAddress.h"
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// including Print.h is deprecated, for all future projects use Arduino.h instead
|
||||||
|
|
||||||
|
// This include is added for compatibility, it will be remove on the next
|
||||||
|
// major release of the API
|
||||||
|
#include "../Print.h"
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// including Printable.h is deprecated, for all future projects use Arduino.h instead
|
||||||
|
|
||||||
|
// This include is added for compatibility, it will be remove on the next
|
||||||
|
// major release of the API
|
||||||
|
#include "../Printable.h"
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// including Server.h is deprecated, for all future projects use Arduino.h instead
|
||||||
|
|
||||||
|
// This include is added for compatibility, it will be remove on the next
|
||||||
|
// major release of the API
|
||||||
|
#include "../Server.h"
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// including Stream.h is deprecated, for all future projects use Arduino.h instead
|
||||||
|
|
||||||
|
// This include is added for compatibility, it will be remove on the next
|
||||||
|
// major release of the API
|
||||||
|
#include "../Stream.h"
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// including Udp.h is deprecated, for all future projects use Arduino.h instead
|
||||||
|
|
||||||
|
// This include is added for compatibility, it will be remove on the next
|
||||||
|
// major release of the API
|
||||||
|
#include "../Udp.h"
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016, Arduino LLC. All Right Reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// including WString.h is deprecated, for all future projects use Arduino.h instead
|
||||||
|
|
||||||
|
// This include is added for compatibility, it will be remove on the next
|
||||||
|
// major release of the API
|
||||||
|
#include "../String.h"
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Standard C functions required in Arduino API
|
||||||
|
// If these functions are not provided by the standard library, the
|
||||||
|
// core should supply an implementation of them.
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern char *itoa(int value, char *string, int radix);
|
||||||
|
extern char *ltoa(long value, char *string, int radix);
|
||||||
|
extern char *utoa(unsigned value, char *string, int radix);
|
||||||
|
extern char *ultoa(unsigned long value, char *string, int radix);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
wiring_pulse.s - pulseInASM() function in different flavours
|
||||||
|
Part of Arduino - http://www.arduino.cc/
|
||||||
|
|
||||||
|
Copyright (c) 2014 Martino Facchin, 2020 Spence Konde
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following routine was generated by avr-gcc 4.8.3 with the following parameters
|
||||||
|
* -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2
|
||||||
|
* on the original C function
|
||||||
|
*
|
||||||
|
* unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops)
|
||||||
|
* {
|
||||||
|
* unsigned long width = 0;
|
||||||
|
* // wait for any previous pulse to end
|
||||||
|
* while ((*port & bit) == stateMask)
|
||||||
|
* if (--maxloops == 0)
|
||||||
|
* return 0;
|
||||||
|
*
|
||||||
|
* // wait for the pulse to start
|
||||||
|
* while ((*port & bit) != stateMask)
|
||||||
|
* if (--maxloops == 0)
|
||||||
|
* return 0;
|
||||||
|
*
|
||||||
|
* // wait for the pulse to stop
|
||||||
|
* while ((*port & bit) == stateMask) {
|
||||||
|
* if (++width == maxloops)
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
|
* return width;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* some compiler outputs were removed but the rest of the code is untouched
|
||||||
|
*
|
||||||
|
* Spence feb 2020: not untouched anymore! The first two loops ran in 11 cycles instead
|
||||||
|
* of 16, so if no pulse was detected, the timeout would be reached when
|
||||||
|
* 11/16ths of the requested timeout had elapsed. This was fixed by the addition
|
||||||
|
* of 2 rjmps to the next line (a 2 cycle nop that uses only 1 word) and 1 nop
|
||||||
|
* to each of these loops before they decrement maxloops.
|
||||||
|
* Additionally, removed duplication of return sequence to save 12b flash
|
||||||
|
* which is conveniently exactly how much the other fix cost.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
.global countPulseASM
|
||||||
|
|
||||||
|
countPulseASM:
|
||||||
|
|
||||||
|
.LM0:
|
||||||
|
.LFBB1:
|
||||||
|
push r12 ; ; 130 pushqi1/1 [length = 1]
|
||||||
|
push r13 ; ; 131 pushqi1/1 [length = 1]
|
||||||
|
push r14 ; ; 132 pushqi1/1 [length = 1]
|
||||||
|
push r15 ; ; 133 pushqi1/1 [length = 1]
|
||||||
|
push r16 ; ; 134 pushqi1/1 [length = 1]
|
||||||
|
push r17 ; ; 135 pushqi1/1 [length = 1]
|
||||||
|
/* prologue: function */
|
||||||
|
/* frame size = 0 */
|
||||||
|
/* stack size = 6 */
|
||||||
|
.L__stack_usage = 6
|
||||||
|
mov r30,r24 ; port, port ; 2 *movhi/1 [length = 2]
|
||||||
|
mov r31,r25 ; port, port
|
||||||
|
/* unsigned long width = 0;
|
||||||
|
*** // wait for any previous pulse to end
|
||||||
|
*** while ((*port & bit) == stateMask)
|
||||||
|
*/
|
||||||
|
.LM1:
|
||||||
|
rjmp .L2 ; ; 181 jump [length = 1]
|
||||||
|
.L4:
|
||||||
|
/* if (--maxloops == 0) */
|
||||||
|
.LM2:
|
||||||
|
rjmp .LM2A ; waste an extra 5 cycles
|
||||||
|
.LM2A:
|
||||||
|
rjmp .LM2B ;
|
||||||
|
.LM2B:
|
||||||
|
nop ;
|
||||||
|
subi r16,1 ; maxloops, ; 17 addsi3/2 [length = 4]
|
||||||
|
sbc r17, r1 ; maxloops
|
||||||
|
sbc r18, r1 ; maxloops
|
||||||
|
sbc r19, r1 ; maxloops
|
||||||
|
breq .L13 ; , ; 19 branch [length = 1]
|
||||||
|
.L2:
|
||||||
|
/* if (--maxloops == 0) */
|
||||||
|
.LM3:
|
||||||
|
ld r25,Z ; D.1554, *port_7(D) ; 22 movqi_insn/4 [length = 1]
|
||||||
|
and r25,r22 ; D.1554, bit ; 24 andqi3/1 [length = 1]
|
||||||
|
cp r25,r20 ; D.1554, stateMask ; 25 *cmpqi/2 [length = 1]
|
||||||
|
breq .L4 ; , ; 26 branch [length = 1]
|
||||||
|
rjmp .L6 ; ; 184 jump [length = 1]
|
||||||
|
.L7:
|
||||||
|
/* return 0;
|
||||||
|
***
|
||||||
|
*** // wait for the pulse to start
|
||||||
|
*** while ((*port & bit) != stateMask)
|
||||||
|
*** if (--maxloops == 0)
|
||||||
|
*/
|
||||||
|
.LM4:
|
||||||
|
rjmp .LM4A ; waste an extra 5 cycles
|
||||||
|
.LM4A:
|
||||||
|
rjmp .LM4B ;
|
||||||
|
.LM4B:
|
||||||
|
nop ;
|
||||||
|
subi r16,1 ; maxloops, ; 31 addsi3/2 [length = 4]
|
||||||
|
sbc r17, r1 ; maxloops
|
||||||
|
sbc r18, r1 ; maxloops
|
||||||
|
sbc r19, r1 ; maxloops
|
||||||
|
breq .L13 ; , ; 33 branch [length = 1]
|
||||||
|
.L6:
|
||||||
|
/* if (--maxloops == 0) */
|
||||||
|
.LM5:
|
||||||
|
ld r25,Z ; D.1554, *port_7(D) ; 41 movqi_insn/4 [length = 1]
|
||||||
|
and r25,r22 ; D.1554, bit ; 43 andqi3/1 [length = 1]
|
||||||
|
cpse r25,r20 ; D.1554, stateMask ; 44 enable_interrupt-3 [length = 1]
|
||||||
|
rjmp .L7 ;
|
||||||
|
mov r12, r1 ; width ; 7 *movsi/2 [length = 4]
|
||||||
|
mov r13, r1 ; width
|
||||||
|
mov r14, r1 ; width
|
||||||
|
mov r15, r1 ; width
|
||||||
|
rjmp .L9 ; ; 186 jump [length = 1]
|
||||||
|
.L10:
|
||||||
|
/* return 0;
|
||||||
|
***
|
||||||
|
*** // wait for the pulse to stop
|
||||||
|
*** while ((*port & bit) == stateMask) {
|
||||||
|
*** if (++width == maxloops)
|
||||||
|
*/
|
||||||
|
.LM6:
|
||||||
|
ldi r24,-1 ; , ; 50 addsi3/3 [length = 5]
|
||||||
|
sub r12,r24 ; width,
|
||||||
|
sbc r13,r24 ; width,
|
||||||
|
sbc r14,r24 ; width,
|
||||||
|
sbc r15,r24 ; width,
|
||||||
|
cp r16,r12 ; maxloops, width ; 51 *cmpsi/2 [length = 4]
|
||||||
|
cpc r17,r13 ; maxloops, width
|
||||||
|
cpc r18,r14 ; maxloops, width
|
||||||
|
cpc r19,r15 ; maxloops, width
|
||||||
|
breq .L13 ; , ; 52 branch [length = 1]
|
||||||
|
.L9:
|
||||||
|
/* if (++width == maxloops) */
|
||||||
|
.LM7:
|
||||||
|
ld r24,Z ; D.1554, *port_7(D) ; 60 movqi_insn/4 [length = 1]
|
||||||
|
and r24,r22 ; D.1554, bit ; 62 andqi3/1 [length = 1]
|
||||||
|
cp r24,r20 ; D.1554, stateMask ; 63 *cmpqi/2 [length = 1]
|
||||||
|
breq .L10 ; , ; 64 branch [length = 1]
|
||||||
|
/* return 0;
|
||||||
|
*** }
|
||||||
|
*** return width;
|
||||||
|
*/
|
||||||
|
.LM8:
|
||||||
|
mov r22,r12 ; D.1553, width ; 108 movqi_insn/1 [length = 1]
|
||||||
|
mov r23,r13 ; D.1553, width ; 109 movqi_insn/1 [length = 1]
|
||||||
|
mov r24,r14 ; D.1553, width ; 110 movqi_insn/1 [length = 1]
|
||||||
|
mov r25,r15 ; D.1553, width ; 111 movqi_insn/1 [length = 1]
|
||||||
|
rjmp .LM11 ;
|
||||||
|
/* epilogue start */
|
||||||
|
.L13:
|
||||||
|
.LM10:
|
||||||
|
ldi r22,0 ; D.1553 ; 120 movqi_insn/1 [length = 1]
|
||||||
|
ldi r23,0 ; D.1553 ; 121 movqi_insn/1 [length = 1]
|
||||||
|
ldi r24,0 ; D.1553 ; 122 movqi_insn/1 [length = 1]
|
||||||
|
ldi r25,0 ; D.1553 ; 123 movqi_insn/1 [length = 1]
|
||||||
|
/* epilogue start */
|
||||||
|
.LM11:
|
||||||
|
pop r17 ; ; 138 popqi [length = 1]
|
||||||
|
pop r16 ; ; 139 popqi [length = 1]
|
||||||
|
pop r15 ; ; 140 popqi [length = 1]
|
||||||
|
pop r14 ; ; 141 popqi [length = 1]
|
||||||
|
pop r13 ; ; 142 popqi [length = 1]
|
||||||
|
pop r12 ; ; 143 popqi [length = 1]
|
||||||
|
ret ; 144 return_from_epilogue [length = 1]
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,86 @@
|
||||||
|
#ifndef __CORE_PARAMETERS_H__
|
||||||
|
#define __CORE_PARAMETERS_H__
|
||||||
|
|
||||||
|
#if !defined(MEGATINYCORE_NUM)
|
||||||
|
#if !(defined(MEGATINYCORE_MAJOR) && defined(MEGATINYCORE_MINOR) && defined(MEGATINYCORE_PATCH) && defined(MEGATINYCORE_RELEASED))
|
||||||
|
#warning "All of the version defines are missing, please correct your build environment; it is likely failing to define other critical values"
|
||||||
|
// Version related defines now handled in platform.txt
|
||||||
|
#else
|
||||||
|
#define MEGATINYCORE_NUM ((MEGATINYCORE_MAJOR << 24) + (MEGATINYCORE_MINOR << 16) + (MEGATINYCORE_PATCH << 8) + MEGATINYCORE_RELEASED)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(defined(MEGATINYCORE) || defined(DXCORE) || defined(ATTIYNCORE))
|
||||||
|
|
||||||
|
// This define can get black-hole'ed somehow (reported on platformio) likely the ugly syntax to pass a string define from platform.txt via a -D
|
||||||
|
// directive passed to the compiler is getting mangled somehow, though I'm amazed it doesn't cause a compile error. But checking for defined(MEGATINYCORE)
|
||||||
|
// is the documented method to detect that megaTinyCore is in use, and without it things that tried to do conditional compilation based on that were not
|
||||||
|
// recognizing it as megaTinyCore and hence would fail to compile when that conditional compilation was required to make it build.
|
||||||
|
// From: https://github.com/adafruit/Adafruit_BusIO/issues/43
|
||||||
|
|
||||||
|
// The whole purpose of this file is largely for for the purpose of being something that can be included anywhere to make sure we know what core we are
|
||||||
|
// which becomes more and more important as more code is shared between the cores.
|
||||||
|
|
||||||
|
#define MEGATINYCORE "Unknown 2.6.9+"
|
||||||
|
#else
|
||||||
|
#if defined(DXCORE) || defined(ATTINYCORE)
|
||||||
|
#error "This is megaTinyCore being compiled, but DXCORE and/or ATTINYCORE is defined already, something is way wrong."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Check for invalid options, mostly to help point the PIO and other third party IDE users in the right direction */
|
||||||
|
|
||||||
|
#if defined(MILLIS_USE_TIMERA0) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERB3) || \
|
||||||
|
defined(MILLIS_USE_TIMERA1) || defined(MILLIS_USE_TIMERD0) || defined(MILLIS_USE_TIMERRTC) || defined(MILLIS_USE_TIMERRTC_XTAL))
|
||||||
|
#error "MILLIS_USE_TIMERA0 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERA1) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERB3) || \
|
||||||
|
defined(MILLIS_USE_TIMERA0) || defined(MILLIS_USE_TIMERD0) || defined(MILLIS_USE_TIMERRTC) || defined(MILLIS_USE_TIMERRTC_XTAL))
|
||||||
|
#error "MILLIS_USE_TIMERA1 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERB0) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERA0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERB3) || \
|
||||||
|
defined(MILLIS_USE_TIMERA1) || defined(MILLIS_USE_TIMERD0) || defined(MILLIS_USE_TIMERRTC) || defined(MILLIS_USE_TIMERRTC_XTAL))
|
||||||
|
#error "MILLIS_USE_TIMERB0 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERB1) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERA0) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERB3) || \
|
||||||
|
defined(MILLIS_USE_TIMERA1) || defined(MILLIS_USE_TIMERD0) || defined(MILLIS_USE_TIMERRTC) || defined(MILLIS_USE_TIMERRTC_XTAL))
|
||||||
|
#error "MILLIS_USE_TIMERB1 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERB2) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERA0) || defined(MILLIS_USE_TIMERB3) || \
|
||||||
|
defined(MILLIS_USE_TIMERA1) || defined(MILLIS_USE_TIMERD0) || defined(MILLIS_USE_TIMERRTC) || defined(MILLIS_USE_TIMERRTC_XTAL))
|
||||||
|
#error "MILLIS_USE_TIMERB2 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERB3) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERA0) || \
|
||||||
|
defined(MILLIS_USE_TIMERA1) || defined(MILLIS_USE_TIMERD0) || defined(MILLIS_USE_TIMERRTC) || defined(MILLIS_USE_TIMERRTC_XTAL))
|
||||||
|
#error "MILLIS_USE_TIMERB3 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERD0) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERB3) || \
|
||||||
|
defined(MILLIS_USE_TIMERA1) || defined(MILLIS_USE_TIMERA0) || defined(MILLIS_USE_TIMERRTC) || defined(MILLIS_USE_TIMERRTC_XTAL))
|
||||||
|
#error "MILLIS_USE_TIMERB5 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERRTC) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERB3) || \
|
||||||
|
defined(MILLIS_USE_TIMERA1) || defined(MILLIS_USE_TIMERD0) || defined(MILLIS_USE_TIMERA0))
|
||||||
|
#error "MILLIS_USE_TIMERE0 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERRTC_XTAL) && (defined(MILLIS_USE_TIMERNONE) || \
|
||||||
|
defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERB3) || \
|
||||||
|
defined(MILLIS_USE_TIMERA1) || defined(MILLIS_USE_TIMERD0) || defined(MILLIS_USE_TIMERA0))
|
||||||
|
#error "MILLIS_USE_TIMERF0 and another timer are set as the millis timer. Specify one only. If this occurred on the Arduino IDE, please report it promptly."
|
||||||
|
#endif
|
||||||
|
#if defined(MILLIS_USE_TIMERE0) || defined(MILLIS_USE_TIMERF0)
|
||||||
|
#error "A millis timer not supported on this core was passed. this should only be possible on third party IDEs. "
|
||||||
|
#endif
|
||||||
|
#if defined(UARTBAUD5V) || defined(UARTBAUD3V)
|
||||||
|
#warning "The UARTBAUDxV baud rate correction was removed in 2.3.0 because it had excessive overhead and negligible benefit. The define you specified does nothing."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,347 @@
|
||||||
|
/* dirty_tricks.h - Where all the ugly asm macros live.
|
||||||
|
* Copyright Spence Konde 2023
|
||||||
|
* Part of DxCore and megaTinyCore, which are open source
|
||||||
|
* and released under the LGPL 2.1
|
||||||
|
* See LICENSE.txt for full legal boilerplate if you must
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Okay... This was getting absurd. We had too many piecemeal hacks to work around shit compiler output, the poor quality of which is was apparent immediately upon
|
||||||
|
* examination of the assembler listings early on, in code that was everywhere, and hence which I had to stare at any time I debugged anything.
|
||||||
|
* We (well, me and the one, maybe two other people here who speak fluent avr asm) all knew we needed unholy tricks like this.
|
||||||
|
* We wish we didn't. But while the GCC part of avr-gcc does a pretty thorough and consistent job optimizing..
|
||||||
|
* The AVR part is piss-poor. It's fucking awful. As I understand it, GCC from a big picture, first transforms the code into an idealized
|
||||||
|
* version that has standins for registers and is architecture agnostic, but tends to correspond very very nearly with machine instructions. Because there is
|
||||||
|
* a massive amount of stuff compiled with GCC and other tools built on the same foundations (they're the only game in town if aren't willing to use a proprietary
|
||||||
|
* one. I don't really understand the appeal of those... ) considerable effort has been directed towards improvement, in
|
||||||
|
* order to improve efficiency of high profile computing applications
|
||||||
|
* This happens in many passes to perform optimization that isn't machine specific (and hence have gotten resources lavished on them by everyone).
|
||||||
|
* Then, it has to go from that ideal world to reality, and start naming instructions and assigning registers. This is machine specific.
|
||||||
|
* And it is here that AVR hasn't gotten much love. The result is sometimes what might be termed pathological assembly (as in, it is indicative disease).
|
||||||
|
* If generated assembly like SBIW r26, 20 immediately followed by ADIW r26, 23 - (or any other constant integers 63 or lower) is not a sign of disease,
|
||||||
|
* I don't know what would be. That's when the compiler has generated code that assumes all three pointer registers can do displacement. Only two of them can,
|
||||||
|
* because there aren't enough unique instructions that can be expressed in 16 bits to implement ldd/std on all three pointer registers (ldd/std consume
|
||||||
|
* a huge number of opcodes. Each pointer worth of opcodes is 1/16th of the total (along with the six immediates plus rcall and rjmp.
|
||||||
|
* That is, these 10 instructions account for 62.5% of the opcodes in the avr instructionset! There are 4096 different opcodes for each of them!)
|
||||||
|
* and that's why while we have 3 pointer registers, we have only two that are fully functional, and which each have 2048 displaced load and 2048 displaced
|
||||||
|
* store opcodes (32 registers * 64 q's from 0-63), making a total of 4096 displaced ld/st's, plus 128 pre/post decremented ones, 4224 in total. The X register, the runt
|
||||||
|
* of the litter, has only ld/st X, ld/st X+ and ld/st -X, a mere 192 opcodes (we could have totally had a 4th pointer, too, in terms of instruction opcodes at least)
|
||||||
|
*
|
||||||
|
* Unfortunately, both Atmel and Microchip have been asleep at the switch here, and not much has been done to make sure that AVR code that gets output isn't terrible
|
||||||
|
* The compiler appears to be
|
||||||
|
* - Often blind to when a pointer is being used to access offsets and hence we should be using the y and z registers with ldd and std
|
||||||
|
* - Inept at combining bytes into larger data types.
|
||||||
|
* - Stupid about isr prologues and epilogs (it looks inside the ISR - and sees what registers need to be saved and restored in order to not corrupt the state of the registers, but it
|
||||||
|
* can't tell if tmp_reg, zero_reg, or SREG are changed by the body of the code so it always saves and restores those)
|
||||||
|
* - bad at bitshifts with multibyte datatypes.
|
||||||
|
*
|
||||||
|
* Since like any good programmer, we have some idea of what our code should be compiling to while writing it, there come to be times when we have to look at
|
||||||
|
* the assembly listing, and when we do we see the so-called optimizer butchering our code in unspeakable ways, wasting precious flash and excecuing slower than
|
||||||
|
* a slug trying to skateboard up a flight of stairs.
|
||||||
|
*
|
||||||
|
* The offences with speed are much larger in magnitude, though we often care less about this (though occasionally we care more about speed than anyything else)
|
||||||
|
* as compared to offenses against flash size, which on embedded systems must always be on our minds.
|
||||||
|
*
|
||||||
|
* To rectify the problems, assorted snippets of inline asm has been written which are an attempt to slap some sense into the compiler, and these have joined more
|
||||||
|
* prosaic assembly macros like the NOP gang.
|
||||||
|
*
|
||||||
|
* The macros were a fucking pain to type (all caps) and the names were both long and inconsistent.
|
||||||
|
*
|
||||||
|
* With this update, we are centralizing and documenting all the dirty assembler tricks.
|
||||||
|
*
|
||||||
|
* I know that mixing lowercase names and macros is sinful. Yes these are macros. Except for the classics (and we have appropriate duplicated of those), they are now lowercase.
|
||||||
|
* I know macros are supposed to be all uppercase. But we're prefixing them with an underscore, so you still know there's spooky shit involved, it's just
|
||||||
|
* too unpleasant to type and too ugly to read to have all these longass macro names, making a mess of all our core "speed matters" code - and besides that,
|
||||||
|
* while macros generally don't care what types they get passed, these totally will cast the arguments to specific types like functions do (actually, significantly more vigorously)
|
||||||
|
*
|
||||||
|
* General Advice:
|
||||||
|
* Don't use any of the wacky ones unless you know what the fuck you're doing. This is largely for internal use within the core, because we do, and we know that
|
||||||
|
* lots of people don't know nor want to know asm (even normal, polite asm, which this often isn't), and thus couldn't reproduce this performance enhancement
|
||||||
|
* if they needed or wanted it, and that describes most people who use the core. Essentially, I feel that when I see a way to silently make the core perform better, even if it looks
|
||||||
|
* heinous, do it, because if you don't, nobody else will, and surely someomne out there needs a bit more performance right?
|
||||||
|
* bit of code - either repeated a lot, and/or in a time-critical section of code that benefits from doing unholy things like this, we're going to do it to make
|
||||||
|
* the core better.
|
||||||
|
*
|
||||||
|
* Many of these hinge upon bullying the compiler into assigning registers less stupidly. These do not at all prevent the register allocator from
|
||||||
|
* smacking down the whole thing with a gigantic wad of fail if it thinks it has to (ie, when you're out of pointer registers). These should be used only with one eye
|
||||||
|
* on the compiler listings to spot this, because at that point they are usually causing harm instead of good.
|
||||||
|
*
|
||||||
|
* List of dirty trick macros
|
||||||
|
* Not dirty - cycle counting macros:
|
||||||
|
* _NOP()
|
||||||
|
* _NOP2()
|
||||||
|
* _NOPNOP()
|
||||||
|
* _NOP8()
|
||||||
|
* _NOP14()
|
||||||
|
*
|
||||||
|
* And in lower case:
|
||||||
|
* _nop()
|
||||||
|
* _nop2()
|
||||||
|
* _nop8()
|
||||||
|
* _nop14()
|
||||||
|
*
|
||||||
|
* Not particularly dirty - AVRs have a single instruction nybble-swap:
|
||||||
|
* _SWAP(n)
|
||||||
|
* _swap(n)
|
||||||
|
*
|
||||||
|
* And now all the hideously dirty ones:
|
||||||
|
* In all cases, pointer-like 16-bit values should be a local variable, either a pointer or uint16_t.
|
||||||
|
* These "tricks" allow us to
|
||||||
|
* _addLow(uint16_t a, uint8_t b)
|
||||||
|
* _subLow(uint16_t a, uint8_t b)
|
||||||
|
* _addHigh(uint16_t a, uint8_t b)
|
||||||
|
* _subHigh(uint16_t a, uint8_t b)
|
||||||
|
* _addLowConst(uint16_t a, const uint8_t b)
|
||||||
|
* _subLowConst(uint16_t a, const uint8_t b)
|
||||||
|
* _addHighConst(uint16_t a, const uint8_t b)
|
||||||
|
* _subHighConst(uint16_t a, const uint8_t b)
|
||||||
|
* _setHigh(uint16_t a, uint8_t b)
|
||||||
|
* _setHighConst(uint16_t a, const uint8_t b)
|
||||||
|
* _clrHigh(uint16_t a)
|
||||||
|
* _setLow(uint16_t a, uint8_t b)
|
||||||
|
* _setLowConst(uint16_t a, const uint8_t b)
|
||||||
|
* _clrLow(uint16_t a)
|
||||||
|
* _fastPtr_z(uint16_t __localVar__, uint16_t __pointer__)
|
||||||
|
* _fastPtr_y(uint16_t __localVar__, uint16_t __pointer__)
|
||||||
|
* _fastPtr_x(uint16_t __localVar__, uint16_t __pointer__)
|
||||||
|
* _fastPtr_d(uint16_t __localVar__, uint16_t __pointer__)
|
||||||
|
* _fastPtr(uint16_t __localVar__, uint16_t __pointer__)
|
||||||
|
* _makeFastPtr_d(uint8_t * newptt, const uint8_t highbyte, uint8_t lowbyte)
|
||||||
|
* _makeFastPtr(uint8_t * newptt, const uint8_t highbyte, uint8_t lowbyte)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DIRTY_TRICKS_H
|
||||||
|
#define DIRTY_TRICKS_H
|
||||||
|
;
|
||||||
|
#ifndef _NOP
|
||||||
|
#define _NOP() __asm__ __volatile__ ("nop")
|
||||||
|
#endif
|
||||||
|
#ifndef _NOP2
|
||||||
|
#define _NOP2() __asm__ __volatile__ ("rjmp .+0")
|
||||||
|
#endif
|
||||||
|
#ifndef _NOPNOP
|
||||||
|
#define _NOPNOP() __asm__ __volatile__ ("rjmp .+0")
|
||||||
|
#endif
|
||||||
|
#ifndef _NOP8
|
||||||
|
#define _NOP8() __asm__ __volatile__ ("rjmp .+2" "\n\t" /* 2 clk jump over next instruction */ \
|
||||||
|
"ret" "\n\t" /* 4 clk return "wha? why here?" */ \
|
||||||
|
"rcall .-4" "\n\t" ) /* 2 clk "Oh, I see. We jump over a return (2 clock) call it, and then immediately return." */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Not enabled. Ugly ways to get delays at very small flash cost.
|
||||||
|
#ifndef _NOP6
|
||||||
|
#define _NOP6() __asm__ __volatile__ ("rcall lonereturn") // 2 bytes of flash. 2+4=6 clk only works if you've got _LONE_RETURN() somewhere. Only guaranteed to work on 8k and smaller parts.
|
||||||
|
#define _NOP7() __asm__ __volatile__ ("call lonereturn") // 4 bytes of flash. 3+4=7 clk and see above, except that this will only work w/>8k flash.
|
||||||
|
#define _LONE_RETURN() __asm__ __volatile__ ("rjmp .+2" "\n\t" \ // 4 bytes of flash overhead, but must exist once and only once for NOP6/7 (but not any others) . Don't trip over thr ret. Note that if you're writing inline assembly with ret elsewhere, just proceed
|
||||||
|
"lonereturn:" "\n\t" \ // it with a a label and jump to it to save 2 bytes vs this methodMust exist somwehere for
|
||||||
|
"ret" "\n\t" )
|
||||||
|
// It could be put into it's own function and marked with the "used" attribute. This allows elimination of the initial rjmp, at the cost of making an ugly hack even uglier.
|
||||||
|
// Or even worse, you have other inline assembly, and you just stick the label right before the return!
|
||||||
|
// Really, these are things you shoudnt do unless you have your back against the flash/RAM limits and a gun to your head.
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Beyond this, just use a loop.
|
||||||
|
* If you don't need submicrosecond accuracy, just use delayMicroseconds(), which uses very similar methods. See Ref_Timers
|
||||||
|
* (eg, ldi (any upper register), n; dec r0; brne .-4)
|
||||||
|
* and pad with rjmp or nop if needed to get target delay.
|
||||||
|
* Simplest form takes a uint8_t and runs for 3n cycles in 3 words. Padded with `nop` or `rjmp .+0`for 3n + 1 or 3n + 2 if outside the loop, 4n or 5n if padded inside the loop
|
||||||
|
* And so on. You will likely end up doing something like
|
||||||
|
*
|
||||||
|
#define CLOCKS_PER_US (F_CPU / 1000000); // preprocessed away
|
||||||
|
#define DELAYCLOCKS (0.8 * CLOCKS_PER_US) // say we wanted a 0.8 us delay.
|
||||||
|
uint8_t x = DELAYCLOCKS / 3; // preprocessed into a constant
|
||||||
|
__asm__ __volatile__ ("dec %0" "\n\t" // before this, an ldi is used to load x into the input operand %0
|
||||||
|
"brne .-4" "\n\t"
|
||||||
|
#if (DELAYCLOCKS % 3 == 2) // 2 clocks extra needed at end
|
||||||
|
"rjmp .+0" "\n\t"
|
||||||
|
#elif (DELAYCLOCKS % 3 == 1) // 1 clock extra needed at end
|
||||||
|
"nop" "\n\t"
|
||||||
|
#endif
|
||||||
|
: "+d"((uint8_t)(x));
|
||||||
|
*
|
||||||
|
* The above will take very close to 0.8us under most any conditions. Notice how all the calculation was moved to the preprocessor.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* You can extend the length of the iterations by adding nop between the dec and brne, and branching 2 bytes further. that makes it 4 clocks per iteration.
|
||||||
|
* You can go for much longer by using 16-bits:
|
||||||
|
* uint16_t x = 2000; * overall takes 8 bytrsd
|
||||||
|
* __asm__ __volatile__ ("sbiw %0,1" "\n\t" // Line is preceded by 2 implied LDI's to fill that upper register pair, Much higher chance of having to push and pop.
|
||||||
|
* \"brne .-4 \"\n\t\" // SBIW takes 2 clocks. branch takes 2 clocks unless it doesn't branch, when it only takes one
|
||||||
|
* : +w"((uint16_t)(x)) // hence this takes 4N+1 clocks (4 per iteration, except for last one which is only 3, plus 2 for the pair of LDI's)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// The fastest way to swap nybbles
|
||||||
|
#ifndef _SWAP
|
||||||
|
#define _SWAP(n) __asm__ __volatile__ ("swap %0" "\n\t" :"+r"((uint8_t)(n)))
|
||||||
|
#endif
|
||||||
|
/* Lowercase versions, in keeping with the theme that will be used for these macros now, to make the code less hideous and easier to type. */
|
||||||
|
#define _swap(n) __asm__ __volatile__ ("swap %0" "\n\t" :"+r"((uint8_t)(n)))
|
||||||
|
#define _nop() __asm__ __volatile__ ("nop")
|
||||||
|
#define _nop2() __asm__ __volatile__ ("rjmp .+0")
|
||||||
|
#define _nop8() __asm__ __volatile__ ("rjmp .+2" "\n\t" "ret" "\n\t" "rcall .-4" "\n\t")
|
||||||
|
#define _nop14() __asm__ __volatile__ ("rjmp .+2" "\n\t" "ret" "\n\t" "rcall .-4" "\n\t" "rcall .-6" "\n\t")
|
||||||
|
|
||||||
|
/*********************/
|
||||||
|
/* Low and high Math */
|
||||||
|
/*********************/
|
||||||
|
/* These are dirty, ugly hacky things. What is the point of them? They save a single clock each when used at a point where they are relevant and correct.
|
||||||
|
By prohibiting the crossing of a 256b barier (the high versions are less useful I daresay...), you save a clock cycle. Note that wraparound behavior is predictable
|
||||||
|
|
||||||
|
A big part of this was just how often the stock code was doing this, over and over and over:
|
||||||
|
adiw r26, 10
|
||||||
|
ld X, rwhatever
|
||||||
|
sbiw r26, 10
|
||||||
|
adiw r26, 12
|
||||||
|
ld X, rsomeother
|
||||||
|
sbit r26, 12
|
||||||
|
ADIW and SBIW are both 2 clock instructions, and a load is 2, so that's 6 clocks; you can do it in 3n+1 instead of 6n clocks as long as they're in the same block of 256 addresses, which is often guaranteed.
|
||||||
|
Other times you may be subtracting values that are mathematically certain to never result in an over or underflow
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _addLow(a,b) __asm__ __volatile__ ("add %0A, %1" "\n\t" :"+r"((uint16_t)(a)):"r"((uint8_t) b))
|
||||||
|
#define _subLow(a,b) __asm__ __volatile__ ("sub %0A, %1" "\n\t" :"+r"((uint16_t)(a)):"r"((uint8_t) b))
|
||||||
|
#define _addHigh(a,b) __asm__ __volatile__ ("add %0B, %1" "\n\t" :"+r"((uint16_t)(a)):"r"((uint8_t) b))
|
||||||
|
#define _subHigh(a,b) __asm__ __volatile__ ("sub %0B, %1" "\n\t" :"+r"((uint16_t)(a)):"r"((uint8_t) b))
|
||||||
|
#define _addLowConst(a,b) __asm__ __volatile__ ("subi %0A, %1" "\n\t" :"+r"((uint16_t)(a)):"M"((uint8_t) (0x00 - ((uint8_t) b))))
|
||||||
|
#define _subLowConst(a,b) __asm__ __volatile__ ("subi %0A, %1" "\n\t" :"+r"((uint16_t)(a)):"M"((uint8_t) b))
|
||||||
|
#define _addHighConst(a,b) __asm__ __volatile__ ("subi %0B, %1" "\n\t" :"+r"((uint16_t)(a)):"M"((uint8_t) (0x00 - ((uint8_t) b))))
|
||||||
|
#define _subHighConst(a,b) __asm__ __volatile__ ("subi %0B, %1" "\n\t" :"+r"((uint16_t)(a)):"M"((uint8_t) b))
|
||||||
|
|
||||||
|
/* _addLow(uint16_t a, uint8_t b) // note that a only needs to be something that can be cast to a 16-bit unsigned value
|
||||||
|
* _subLow(uint16_t a, uint8_t b) // It's type is not changed, but it's value is changed, and the math is done as though it were a uint16_t
|
||||||
|
* _addHigh(uint16_t a, uint8_t b) // Much less often useful - this is exactly analogous to above, except operating on high byte
|
||||||
|
* _subHigh(uint16_t a, uint8_t b) // But you do occasionally need to do it.
|
||||||
|
*
|
||||||
|
* These perform a subtraction on the low (or high) byte. That's it. They do not make any sort of attempt to handle over/underflow.
|
||||||
|
* It saves a clock cycle and an instruction word when you are doing math where are adding or subtracting an 8-bit value to/from a 16-bit one.
|
||||||
|
* but you KNOW there will never be a carry or borrow, and thus, you can do the math on the low byte in isolation.
|
||||||
|
* Why do you want to do this? Probably because the thing you're adding or subtracting a little to/from is a pointer to a memory address, like
|
||||||
|
* the start of an instance of a peripheral and you're adding the offset to it. The peripherals are all aligned on 16, 32, 64, or 128 byte bounds
|
||||||
|
* and have at most that many registers. There may be times when you have 256-byte aligned pointers, too. When you want to offset them and either
|
||||||
|
* you need an offset of more than 63, or you know that you're already out of pointer pairs that can do displacement (and you're not working
|
||||||
|
* with a straight up constant. If the address can be constant-folded, and you are only using it once, this doesn't help and actually makes it worse)
|
||||||
|
*
|
||||||
|
* In that case, while normally you'd do:
|
||||||
|
*
|
||||||
|
* uint8_t foo = *(base_pointer + 17);
|
||||||
|
*
|
||||||
|
* and it would perform an std z+17 or std y+17 depending on whether it's in the z or y pointer.
|
||||||
|
* But std requires q to be 0-63 (you may need more), and the base pointer to be y or z (you may know that those two pointers have to already be
|
||||||
|
* in use), and that the offset be constant (if it was, say, a number between 0x10 and 0x17 because you're writing to a PORTx.PINnCTRL register)
|
||||||
|
* If x and y are unavailable but the offset is constant, then you get a 2-word 4-clock penalty as std becomes adiw, st, sbiw. If the offset isn't
|
||||||
|
* constant, it's add adc st sub subc, still 4 clock penalty, but now 4 words too (same if it's a constant larger than 63, except with subi instead
|
||||||
|
* of add and sub) This reduces it to 1 clock and 1 word - though you need to remember it's now pointing to a different place, and correct for that
|
||||||
|
* next time you use it.
|
||||||
|
* (note: If base_pointer is a compile time constant and is used only once it's better to just do the normal way too, because then it comes out as a
|
||||||
|
* 2-word 2-clock sts, instead of 2 words to ldi the address then one more to st to it).
|
||||||
|
*
|
||||||
|
* Finally, be aware that you're still going to get a lot of overhead (though some is not avoidable) if you are already using all three pointers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************/
|
||||||
|
/* Low and high Set */
|
||||||
|
/*********************/
|
||||||
|
#define _setHigh(a,b) __asm__ __volatile__ ("mov %0B, %1" "\n\t" :"+r"((uint16_t)(a)):"r"((uint8_t) b))
|
||||||
|
#define _setHighConst(a,b) __asm__ __volatile__ ("ldi %0B, %1" "\n\t" :"+d"((uint16_t)(a)):"M"((uint8_t) b))
|
||||||
|
#define _clrHigh(a) __asm__ __volatile__ ("eor %0B, %0B" "\n\t" :"+r"((uint16_t)(a)))
|
||||||
|
#define _setLow(a,b) __asm__ __volatile__ ("mov %0A, %1" "\n\t" :"+r"((uint16_t)(a)):"r"((uint8_t) b))
|
||||||
|
#define _setLowConst(a,b) __asm__ __volatile__ ("ldi %0A, %1" "\n\t" :"+d"((uint16_t)(a)):"M"((uint8_t) b))
|
||||||
|
#define _clrLow(a) __asm__ __volatile__ ("eor %0A, %0A" "\n\t" :"+r"((uint16_t)(a)))
|
||||||
|
/* _setHigh(uint16_t a, uint8_t b)
|
||||||
|
* _setHighConst(uint16_t a, const uint8_t b)
|
||||||
|
* _clrHigh(uint16_t a)
|
||||||
|
* _setLow(uint16_t a, uint8_t b)
|
||||||
|
* _setLowConst(uint16_t a, const uint8_t b)
|
||||||
|
* _clrLow(uint16_t a)
|
||||||
|
*
|
||||||
|
* Note that a can be any 16-bit value (likely a pointer), and it will be changed in place.
|
||||||
|
*
|
||||||
|
* Constants must use the const version, otherwise you will pay a 1-clock 1-word penalty, and you were trying to avoid that.
|
||||||
|
*
|
||||||
|
* These allow you to perform surgery on a 16-bit values that already exist, where you wish to change the high or low byte
|
||||||
|
* without altering the other byte. Amazingly, the compiler doesn't optimize this like you'd expect it to (likely because
|
||||||
|
* of C type promotion rules).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/************/
|
||||||
|
/* _fastPtr */
|
||||||
|
/************/
|
||||||
|
#define _checkType(_varA_, _varB_) if (!__builtin_types_compatible_p(__typeof__(_varA_), __typeof__(_varB_))) { badArg("Non-compatible types"); }
|
||||||
|
#define _fastPtr_z(_localVar_, _pointer_) _checkType(_localVar_, _pointer_); __asm__ __volatile__("\n\t": "=&z"(_localVar_) : "0"(_pointer_)); // r30:r31
|
||||||
|
#define _fastPtr_y(_localVar_, _pointer_) _checkType(_localVar_, _pointer_); __asm__ __volatile__("\n\t": "=&y"(_localVar_) : "0"(_pointer_)); // r28:r29
|
||||||
|
#define _fastPtr_x(_localVar_, _pointer_) _checkType(_localVar_, _pointer_); __asm__ __volatile__("\n\t": "=&x"(_localVar_) : "0"(_pointer_)); // r26:r27
|
||||||
|
#define _fastPtr_d(_localVar_, _pointer_) _checkType(_localVar_, _pointer_); __asm__ __volatile__("\n\t": "=&b"(_localVar_) : "0"(_pointer_)); // Y or Z
|
||||||
|
#define _fastPtr(_localVar_, _pointer_) _checkType(_localVar_, _pointer_); __asm__ __volatile__("\n\t": "=&e"(_localVar_) : "0"(_pointer_)); // X,Y or Z
|
||||||
|
/* _fastPtr_z(__localVar__, __pointer__)
|
||||||
|
* _fastPtr_y(__localVar__, __pointer__)
|
||||||
|
* _fastPtr_x(__localVar__, __pointer__)
|
||||||
|
* _fastPtr_d(__localVar__, __pointer__)
|
||||||
|
* _fastPtr(__localVar__, __pointer__)
|
||||||
|
*
|
||||||
|
* GCC fails to optimize memory accesses on AVR.
|
||||||
|
* When accessing memory, GCC is using STS/LDS, using 2 words per instruction and 2/3 clocks respectively.
|
||||||
|
* In some cases, the programmer knows in advance that the function will access multiple peripheral registers or a bigger struct. (<= +63)
|
||||||
|
* To convince GCC that loading the pointer in advance will be more efficient, this defines are provided. The inline assembly forces GCC
|
||||||
|
* To use the instruction STD/LDD that are just one word big and need only 1/2 clocks respectively, allowing for smaller code and faster execution.
|
||||||
|
* It should be considered, that the address has to be loaded into the register first, thus adding 2 words and 2 clocks overhead. Not to mention the compiler
|
||||||
|
* might use the register for something else already. As GCC simulates Displacement on X with adiw/sbiw, it is not recommended to use "E" or "X" for
|
||||||
|
* optimizations. They were added for completion.
|
||||||
|
*
|
||||||
|
* How to use:
|
||||||
|
* declare local pointer variable with the same type as the original pointer, e.g. ADC_t* pADC;
|
||||||
|
* use one of the defines below. e.g. FORCE_LOAD_POINTER_IN_B(pADC, &ADC0)
|
||||||
|
* and voila! all memory accesses to ADC0 are, e.g. Y+xx or Z+xx
|
||||||
|
* it is also possible to use a function argument as __localVar__ and __pointer__ (see twi.c slave ISR)
|
||||||
|
*
|
||||||
|
* How do the instructions work?
|
||||||
|
* '=' is a modifier, that signals the compiler that whatever was in the register beforehand will be overwritten. The compiler should save the value, if it will be needed afterwards
|
||||||
|
* '&' is a modifier, that makes the compiler add a prequel to the assembly where the value (the one in brackets) is loaded in the specified register
|
||||||
|
* 'z' is a constraint that tells the compiler what register to load a value into, in this case, the Z register.
|
||||||
|
* '0' is a matching constraint. It tells the compiler that this input constraint is the same thing as the (number-) matching output operand. basically saying __localVar__ = __pointer__;
|
||||||
|
* Note: The compiler is not prevented from shitting on these - it can still overrule you by moving the pointer to the register you ask and then moving it out, but if it's doing that,
|
||||||
|
* the overhead is probably not avoidable: you filled up all the pointers so no matter what happens will probably be bad; hence why we recommend keeping one eye on the asm listing.
|
||||||
|
* If you think you can do better in that scenario, you're probably going to have to write some assembly. At least it's AVR not something horrible like PIC!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/****************/
|
||||||
|
/* _makeFastPtr */
|
||||||
|
/****************/
|
||||||
|
#define _makeFastPtr_d(newptt, highbyte, lowbyte) (__asm__ __volitile("ldi %0B, %1 " "\n\t" "mov %0A, %2 " "\n\t":"+b"((uint16_t) newptr):"M" ((uint8_t) highbyte), "r" ((uint8_t ), lowbyte)
|
||||||
|
#define _makeFastPtr(newptt, highbyte, lowbyte) (__asm__ __volitile("ldi %0B, %1 " "\n\t" "mov %0A, %2 " "\n\t":"+e"((uint16_t) newptr):"M" ((uint8_t) highbyte), "r" ((uint8_t ), lowbyte)
|
||||||
|
/* _makeFastPtr_d(uint8_t * newptt, const uint8_t highbyte, uint8_t lowbyte)
|
||||||
|
* _makeFastPtr(uint8_t * newptt, const uint8_t highbyte, uint8_t lowbyte)
|
||||||
|
*
|
||||||
|
* Very similar to the above. Passed a pointer as two bytes, the high byte constant and the low byte not, this finds use in the same sort
|
||||||
|
* of situatios as high/low math. See the I/O headers: each class of peripherals often has the same high byte for all addresses. Ports (0x0400, 0x0420, 0x0440
|
||||||
|
* and so on. The most frequently used functions get special attention paid to this as a small gain adds up for the most commonly called functions
|
||||||
|
* usage is typically sdmething like
|
||||||
|
* lowbyte = (_SWAP(usartnbr)); // passed from,elsewhere io the code, which must be free of bugs!
|
||||||
|
* lowbyte <<= 1;
|
||||||
|
* volatile uint8_t *myptr l;
|
||||||
|
* _makeFastPtr_d(myptr. 0x08, lowbyte)
|
||||||
|
* Now, myptr points to the start of the specified USART, in just 2 clock cycles (4 counting the setup in the example), instead of the typical 4 (6) the compiler creates.
|
||||||
|
* This, like most of this file, is the sort of thing you should use under the most constrained conditions only (in an ISR, or a core function that gets called right and left
|
||||||
|
* or extreme-time-sensitive moments, or when you are using a part with less flash than you should, can't get one with larger flash (either they don't exist, or are
|
||||||
|
* backordered past next year), and you just desperately need a tiiiiiiny bit more flash, but you've exhausted the usual methods.
|
||||||
|
*
|
||||||
|
* SK: I've totally been desperate enough on a 16k part that I was replacing double-quoted strings 1 character long with single quoted characters
|
||||||
|
* in order to get rid of the space the null took up and pulling strings from progmem to RAM because that saved a word vs printing it with F() macro
|
||||||
|
* (it was a classic AVR, no memory mapped flash). I was told 500 of them were getting programmed the next day and we needed a feature added. One that I suspected
|
||||||
|
* weeks ago would be needed, but they'd finally realized it the evening before D-day at around 9pm with a few hundred byes of flash left. Good times! Would have been cake if I knew ASM then
|
||||||
|
* since then I could optimize the code.
|
||||||
|
*
|
||||||
|
* _makeFastPtr() should be used when you are only reading from that one address, or consecutive addresses starting there (eg, *ptr++ or *--ptr),
|
||||||
|
* so it can use the X, Y or Z register.
|
||||||
|
* _makeFastPtr_d() uses only X or Y register, and should be used if you expect to be accessing constant compile time known constant offsets
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,232 @@
|
||||||
|
#ifndef _ERRATA_H
|
||||||
|
#define _ERRATA_H
|
||||||
|
|
||||||
|
#if defined(__AVR_Tiny_0__)
|
||||||
|
/* None of these have ever gotten a die rev that fixed bugs - the only "erratum"
|
||||||
|
* that they fixed involved something written during factory cal.
|
||||||
|
*/
|
||||||
|
#if _AVR_FLASH == 16 || _AVR_FLASH == 8 /* 1604, 1606, 1607, 804, 806, 807 */
|
||||||
|
|
||||||
|
#define ERRATA_CCL_OUTEN (-1)
|
||||||
|
#define ERRATA_CCL_DLATCH (-1)
|
||||||
|
#define ERRATA_RTC_PITANDRTC (-1)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (-1)
|
||||||
|
#define ERRATA_USART_WAKE (-1)
|
||||||
|
#define ERRATA_ADC_FREERUN (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_TCB_SYNCUPD (-1)
|
||||||
|
#define ERRATA_TCB_PULSELEN (-1)
|
||||||
|
#else /* 202, 204, 402, 404, 406 */
|
||||||
|
#define ERRATA_CCL_OUTEN (-1)
|
||||||
|
#define ERRATA_CCL_DLATCH (-1)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_ADC_FREERUN (-1)
|
||||||
|
#define ERRATA_RTC_PITANDRTC (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_TCB_SYNCUPD (-1)
|
||||||
|
#define ERRATA_TCB_PULSELEN (-1)
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (-1)
|
||||||
|
#define ERRATA_USART_WAKE (-1)
|
||||||
|
#endif
|
||||||
|
#define ERRATA_USART_FRAMING (0)
|
||||||
|
#define ERRATA_TCD_ASYNC_COUNTPSC (-128) // Tiny 0 has no TCD
|
||||||
|
#define ERRATA_TCD_HALTANDRESTART (-128)
|
||||||
|
#define ERRATA_TCD_AUTOUPDATE (-128)
|
||||||
|
#define ERRATA_TWI_ASSORTED (0)
|
||||||
|
#define ERRATA_USART_ISFIF (0)
|
||||||
|
#define ERRATA_ADC_WCMP (0)
|
||||||
|
#define ERRATA_AC_FALSETRIGGER (0)
|
||||||
|
#define ERRATA_AC_INTFLAG (0)
|
||||||
|
#define ERRATA_ADC_ASDVSAMPLEN (0)
|
||||||
|
#define ERRATA_ADC_FLAGSRESH (0)
|
||||||
|
#define ERRATA_TWI_PINS (0) /* Not in any errata? Huh? */
|
||||||
|
#define ERRATA_TWI_FLUSH (0) /* Flush bug hasn't showed up on any 1-series errata */
|
||||||
|
#define ERRATA_ADC_LOWLAT (-128) /* There's no LOWLAT here cause the ADC doesn't have it*/
|
||||||
|
#elif defined(__AVR_Tiny_1__)
|
||||||
|
#if _AVR_FLASH == 32 /* 3216 and 3217
|
||||||
|
* Them lucky 32k parts, they get all the breaks! Not only did they start with a less buggy design overall,
|
||||||
|
* they also have given it a die rev (to Rev. C - Rev. B was never shipped)
|
||||||
|
*/
|
||||||
|
#define ERRATA_CCL_OUTEN (2)
|
||||||
|
#define ERRATA_CCL_DLATCH (2)
|
||||||
|
#define ERRATA_RTC_PITANDRTC (2)
|
||||||
|
#define ERRATA_AC_FALSETRIGGER (0)
|
||||||
|
#define ERRATA_AC_INTFLAG (0)
|
||||||
|
#define ERRATA_USART_ISFIF (-2)
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (-1)
|
||||||
|
#define ERRATA_USART_FRAMING (2)
|
||||||
|
#define ERRATA_USART_WAKE (-2)
|
||||||
|
#define ERRATA_ADC_FREERUN (2)
|
||||||
|
#define ERRATA_AC_INTFLAG (0)
|
||||||
|
#define ERRATA_TCD_AUTOUPDATE (0)
|
||||||
|
#define ERRATA_ADC_ASDVSAMPLEN (0)
|
||||||
|
#define ERRATA_ADC_FLAGSRESH (0)
|
||||||
|
#define ERRATA_ADC_WCMP (0)
|
||||||
|
#define ERRATA_TWI_ASSORTED (0)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_TCB_SYNCUPD (-1)
|
||||||
|
#define ERRATA_TCB_PULSELEN (-1)
|
||||||
|
#define ERRATA_TCD_ASYNC_COUNTPSC (-1)
|
||||||
|
#define ERRATA_TCD_HALTANDRESTART (-1)
|
||||||
|
#elif _AVR_FLASH == 16 /* 1614, 1616, 1617
|
||||||
|
* The first and the worst - 34 errata are listed officially.
|
||||||
|
*/
|
||||||
|
#define ERRATA_FUSE_OSCLOCK (-1)
|
||||||
|
#define ERRATA_CCL_OUTEN (-1)
|
||||||
|
#define ERRATA_CCL_DLATCH (-1)
|
||||||
|
#define ERRATA_RTC_PITANDRTC (-1)
|
||||||
|
#define ERRATA_AC_FALSETRIGGER (1)
|
||||||
|
#define ERRATA_AC_INTFLAG (-1)
|
||||||
|
#define ERRATA_USART_ISFIF (0)
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (-1)
|
||||||
|
#define ERRATA_USART_FRAMING (-1)
|
||||||
|
#define ERRATA_USART_WAKE (0)
|
||||||
|
#define ERRATA_TCD_AUTOUPDATE (1)
|
||||||
|
#define ERRATA_ADC_FREERUN (-1)
|
||||||
|
#define ERRATA_ADC_ASDVSAMPLEN (-1)
|
||||||
|
#define ERRATA_ADC_FLAGSRESH (-1)
|
||||||
|
#define ERRATA_ADC_WCMP (-1)
|
||||||
|
#define ERRATA_TWI_ASSORTED (-1)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_TCB_SYNCUPD (-1)
|
||||||
|
#define ERRATA_TCB_PULSELEN (-1)
|
||||||
|
#define ERRATA_TCD_ASYNC_COUNTPSC (-1)
|
||||||
|
#define ERRATA_TCD_HALTANDRESTART (-1)
|
||||||
|
#elif _AVR_FLASH == 8 || _AVR_PINCOUNT == 24 /* 814, 816, 817, 417
|
||||||
|
* These are the second worst off of the 1-series.
|
||||||
|
*/
|
||||||
|
#define ERRATA_CCL_OUTEN (-1)
|
||||||
|
#define ERRATA_CCL_DLATCH (-1)
|
||||||
|
#define ERRATA_RTC_PITANDRTC (-1)
|
||||||
|
#define ERRATA_AC_FALSETRIGGER (-1)
|
||||||
|
#define ERRATA_AC_INTFLAG (-1)
|
||||||
|
#define ERRATA_USART_ISFIF (-1)
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (-1)
|
||||||
|
#define ERRATA_USART_FRAMING (-1)
|
||||||
|
#define ERRATA_USART_WAKE (-1)
|
||||||
|
#define ERRATA_ADC_FREERUN (-1)
|
||||||
|
#define ERRATA_AC_INTFLAG (-1)
|
||||||
|
#define ERRATA_TCD_AUTOUPDATE (-1)
|
||||||
|
#define ERRATA_ADC_ASDVSAMPLEN (0)
|
||||||
|
#define ERRATA_ADC_FLAGSRESH (0)
|
||||||
|
#define ERRATA_ADC_WCMP (-1)
|
||||||
|
#define ERRATA_TWI_ASSORTED (-1)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_TCB_SYNCUPD (-1)
|
||||||
|
#define ERRATA_TCB_PULSELEN (-1)
|
||||||
|
#define ERRATA_TCD_ASYNC_COUNTPSC (-1)
|
||||||
|
#define ERRATA_TCD_HALTANDRESTART (-1)
|
||||||
|
#else /* 212, 214, 412, 414, 416.
|
||||||
|
* There's a Rev. C, but they didn't fix any bugs \o/
|
||||||
|
* But Rev B fixed a number of them at least.
|
||||||
|
*/
|
||||||
|
#define ERRATA_CCL_OUTEN (-1)
|
||||||
|
#define ERRATA_CCL_DLATCH (-1)
|
||||||
|
#define ERRATA_RTC_PITANDRTC (-1)
|
||||||
|
#define ERRATA_AC_FALSETRIGGER (1)
|
||||||
|
#define ERRATA_USART_ISFIF (0)
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (-1)
|
||||||
|
#define ERRATA_USART_FRAMING (-1)
|
||||||
|
#define ERRATA_USART_WAKE (-1)
|
||||||
|
#define ERRATA_ADC_FREERUN (1)
|
||||||
|
#define ERRATA_AC_INTFLAG (1)
|
||||||
|
#define ERRATA_TCD_AUTOUPDATE (0)
|
||||||
|
#define ERRATA_ADC_ASDVSAMPLEN (1)
|
||||||
|
#define ERRATA_ADC_FLAGSRESH (1)
|
||||||
|
#define ERRATA_ADC_WCMP (1)
|
||||||
|
#define ERRATA_TWI_ASSORTED (1)
|
||||||
|
#define ERRATA_TCB_ICFPWM (1)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_TCB_SYNCUPD (-1)
|
||||||
|
#define ERRATA_TCB_PULSELEN (-1)
|
||||||
|
#define ERRATA_TCD_ASYNC_COUNTPSC (-1)
|
||||||
|
#define ERRATA_TCD_HALTANDRESTART (-1)
|
||||||
|
#endif
|
||||||
|
#define ERRATA_TWI_PINS (0) /* Not in any errata? Huh? */
|
||||||
|
#define ERRATA_TWI_FLUSH (0) /* Flush bug hasn't showed up on any 1-series errata */
|
||||||
|
#define ERRATA_ADC_LOWLAT (-128) /* There's no LOWLAT here */
|
||||||
|
#elif defined(__AVR_Tiny_2__)
|
||||||
|
#define ERRATA_TCB_ICFPWM (0)
|
||||||
|
#define ERRATA_CCL_OUTEN (0)
|
||||||
|
#define ERRATA_CCL_DLATCH (0)
|
||||||
|
#define ERRATA_RTC_PITANDRTC (0)
|
||||||
|
#define ERRATA_AC_FALSETRIGGER (0)
|
||||||
|
#define ERRATA_USART_FRAMING (0)
|
||||||
|
#define ERRATA_USART_WAKE (0)
|
||||||
|
#define ERRATA_ADC_FREERUN (0)
|
||||||
|
#define ERRATA_AC_INTFLAG (0)
|
||||||
|
#define ERRATA_TCD_AUTOUPDATE (0)
|
||||||
|
#define ERRATA_ADC_ASDVSAMPLEN (0)
|
||||||
|
#define ERRATA_ADC_FLAGSRESH (0)
|
||||||
|
#define ERRATA_ADC_WCMP (0)
|
||||||
|
#define ERRATA_TWI_ASSORTED (0)
|
||||||
|
#define ERRATA_TCB_ICFPWM (0)
|
||||||
|
#define ERRATA_TCA_RESTART (0)
|
||||||
|
#define ERRATA_TCB_SYNCUPD (0)
|
||||||
|
#define ERRATA_TCB_PULSELEN (0) /* No TCD */
|
||||||
|
#define ERRATA_TCD_ASYNC_COUNTPSC (0) /* No TCD */
|
||||||
|
#define ERRATA_TCD_HALTANDRESTART (0) /* No TCD */
|
||||||
|
#define ERRATA_TWI_PINS (0) /* Not in any errata? Huh? */
|
||||||
|
#define ERRATA_TWI_FLUSH (0) /* Flush bug hasn't showed up on any 2-series errata */
|
||||||
|
#if _AVR_FLASH == 32
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (0)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_USART_ISFIF (-1)
|
||||||
|
#define ERRATA_ADC_LOWLAT (-1)
|
||||||
|
#elif _AVR_FLASH == 16
|
||||||
|
/* 16k parts also have excessive current consumption in power-down mode */
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (-1)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_USART_ISFIF (-1)
|
||||||
|
#define ERRATA_ADC_LOWLAT (-1)
|
||||||
|
#else /* 8k and 4k 2-series share die */
|
||||||
|
#define ERRATA_USART_ONEWIRE_PINS (0)
|
||||||
|
#define ERRATA_CCL_PROTECTION (-1)
|
||||||
|
#define ERRATA_TCA_RESTART (-1)
|
||||||
|
#define ERRATA_TCB_CCMP (-1)
|
||||||
|
#define ERRATA_USART_ISFIF (-1)
|
||||||
|
#define ERRATA_ADC_LOWLAT (-1)
|
||||||
|
#endif
|
||||||
|
#define ERRATA_TCD_ASYNC_COUNTPSC (-128)
|
||||||
|
#define ERRATA_TCD_AUTOUPDATE (-128)
|
||||||
|
#define ERRATA_TCD_HALTANDRESTART (-128)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline bool __attribute__((always_inline)) checkErrata(int8_t errata) {
|
||||||
|
/* We want
|
||||||
|
errata = 0, return 0/false.
|
||||||
|
errata = -1, return 1/true.
|
||||||
|
errata =
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!(errata & 0x7F)) { // 0 or(-128)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#if defined(__AVR_Tiny_1__) && _AVR_FLASH == 32
|
||||||
|
// These parts gained an 2 errata
|
||||||
|
uint8_t revid = SYSCFG.REVID;
|
||||||
|
if (errata == -2) {
|
||||||
|
return revid > 2;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
uint8_t revid = SYSCFG.REVID;
|
||||||
|
#endif
|
||||||
|
return revid < errata;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2012 Arduino. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty yield() hook.
|
||||||
|
*
|
||||||
|
* This function is intended to be used by library writers to build
|
||||||
|
* libraries or sketches that supports cooperative threads.
|
||||||
|
*
|
||||||
|
* Its defined as a weak symbol and it can be redefined to implement a
|
||||||
|
* real cooperative scheduler.
|
||||||
|
*/
|
||||||
|
static void __empty() {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
void yield(void) __attribute__ ((weak, alias("__empty")));
|
|
@ -0,0 +1,200 @@
|
||||||
|
/* main.cpp - Main loop for Arduino sketches
|
||||||
|
Part of megaTinyCore - github.com/SpenceKonde/megaTinyCore
|
||||||
|
Copyright (c) 2018~2021 Spence Konde, (c) 2005-2013 Arduino
|
||||||
|
Free Software - LGPL 2.1, please see LICENCE.md for details */
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
/* Required by some libraries to compile successfully. Even though it's nonsense in Arduino. */
|
||||||
|
int atexit(void ( * /*func*/)()) { return 0; }
|
||||||
|
|
||||||
|
void initVariant() __attribute__((weak));
|
||||||
|
/* Weak empty variant initialization function. The purpose is unclear. It sounds like it was intended
|
||||||
|
* initialize the variant, and specific variants would have their own implementation. But in practice
|
||||||
|
* it seems to be instead used as an initialization callback that libraries can use to run code before
|
||||||
|
* setup, like FreeRTOS - it would have been nice if the official API included such a callback. */
|
||||||
|
void initVariant() { }
|
||||||
|
|
||||||
|
void __attribute__((weak)) onPreMain();
|
||||||
|
void __attribute__((weak)) onPreMain() {
|
||||||
|
/* Override with any user code that needs to run WAY early, in .init3 */
|
||||||
|
}
|
||||||
|
void __attribute__((weak)) onBeforeInit();
|
||||||
|
void __attribute__((weak)) onBeforeInit() {
|
||||||
|
/* Override with any user code that needs to run before init() */
|
||||||
|
}
|
||||||
|
uint8_t __attribute__((weak)) onAfterInit();
|
||||||
|
uint8_t __attribute__((weak)) onAfterInit() {
|
||||||
|
/* Override with any user code that needs to run before interrupts are
|
||||||
|
* enabled but after all other core initialization.
|
||||||
|
* return 1 to not enable interrupts) */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main() __attribute__((weak));
|
||||||
|
/* The main function - call initialization functions (in wiring.c) then setup, and finally loop *
|
||||||
|
* repeatedly. If SerialEvent is enabled (which should be unusual, as it is no longer a menu *
|
||||||
|
* option even, that gets checked for after each call to loop). Note that _pre_main() is *
|
||||||
|
* called first in non-optiboot configurations (neither is needed on Optibooot configurations() *
|
||||||
|
* an extra bit of initialization code in .init3 to fix the vectors and still happen if user *
|
||||||
|
* overrides main. In the past there was a USB-related function here, that is removed, as work *
|
||||||
|
* will be needed in any event at the core level if VUSB-based "stuff" arrives, but really I'm *
|
||||||
|
* just waiting for the DU-series now */
|
||||||
|
int main() {
|
||||||
|
onBeforeInit(); // Emnpty callback called before init but after the .init stuff. First normal code executed
|
||||||
|
init(); // Interrupts are turned on just prior to init() returning.
|
||||||
|
initVariant();
|
||||||
|
if (!onAfterInit()) sei(); // enable interrupts.
|
||||||
|
setup();
|
||||||
|
for (;;) {
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if (!defined(USING_OPTIBOOT))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** CHECK RESET FLAGS ******************************************/
|
||||||
|
/* If we are not using Optiboot, we need to check the reset flagss, and reset via software for *
|
||||||
|
* a clean start. Unfortunately, if we clear the registers here, we'll prevent user code from *
|
||||||
|
* seeing them, which isn't helpful. As documented in the reset guide, we suggest overriding *
|
||||||
|
* this function with your own version. One example is included below and others in that guide *
|
||||||
|
* init_reset_Flags() should be overridden with one of the ones from the reset guide in any *
|
||||||
|
* production code. *
|
||||||
|
* If using optiboot, this will never be called, because Optiboot does the same thing. *
|
||||||
|
* By the time app runs, the flags will have been cleared and moved to GPIOR0* (it needs to *
|
||||||
|
* clear flags to honor bootloader entry conditions, so I didn't have a choice about that. *
|
||||||
|
* This function is called before *anything* else, so the chip is a blank slate - or it's *
|
||||||
|
* state is unknown. You're probably running at 4 MHz unless it was a dirty reset, in which *
|
||||||
|
* case it could be anything. No timekeeping is possible, period. The only exception is the *
|
||||||
|
* WDT reset timer with is independent of the HF oscillators and is designed to reset you out *
|
||||||
|
* of hangs amd bad states fthat you end up with when a bug causes the code but not the *
|
||||||
|
* hardware to reset, *
|
||||||
|
* Interrupts are disabled, or in event of dirty reset *
|
||||||
|
* LVL0EX bit will block them. In the event of a clean reset, nothing is set up. There is no *
|
||||||
|
* PWM, no timekeeping of any millis/micros/delay see no time passing and and all delays, *
|
||||||
|
* delay_microseconds, _delay_ms() and _delay_us(), are the wrong length because they are *
|
||||||
|
* based on F_CPU. *
|
||||||
|
* If you end up here from a dirty reset, you know nothing about the configuration of the *
|
||||||
|
* peripherals. Check the flags, save them if you need them, and maybe turn on an LED while *
|
||||||
|
* waiting for the WDT to trigger. If you're debugging something really nasty, you can try to *
|
||||||
|
* gather data about the nature of the fault. For example, turn on an LED if\ LVL0EX is set *
|
||||||
|
* meaning you got here from a missing ISR. With one of those little boards with 6 LEDs on *
|
||||||
|
* (many are available reasonably cheaply on aliexpress et al.) end up being very useful *
|
||||||
|
* for this sort of thing.
|
||||||
|
*
|
||||||
|
* * The register in question is GPIOR0 on megaTinyCore, GPR.GPR0 on Dx-series, but both names *
|
||||||
|
* are aliases of each other per core_devices for compatibility
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Minimum: Reset if we wound up here through malfunction - this relies on user clearing the *
|
||||||
|
* register on startup, which is rarely done in Arduino land. */
|
||||||
|
void __attribute__((weak)) init_reset_flags() ;
|
||||||
|
void __attribute__((weak)) init_reset_flags() {
|
||||||
|
uint8_t flags = RSTCTRL.RSTFR;
|
||||||
|
RSTCTRL.RSTFR = flags;
|
||||||
|
if (flags == 0) {
|
||||||
|
_PROTECTED_WRITE(RSTCTRL.SWRR, 1);
|
||||||
|
}
|
||||||
|
GPIOR0 = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If using SPM from app, but not actually using Optiboot, we need to point the vector tables in the right place.
|
||||||
|
* since the "application" is actually split across "boot" and "application" pages of flash... and it's vectors
|
||||||
|
* are all in the section defined as "boot" section, tell the interrupt controller that, otherwise nothing'll work!
|
||||||
|
* This could just as well be set in init() but for the fact that we support overriding main(). I don't know if
|
||||||
|
* anyone who is doing that wants to use my flashwrite library, but it seems plausible.
|
||||||
|
* And while we way you need to take full responsibility for setting up the part if you do, nobody is going
|
||||||
|
* to figure this out; that's not a reasonable expectation.
|
||||||
|
* We also at the same time make sure there's a reset flag. We can't clear it, even though that
|
||||||
|
* needs to be done becauwe then it wouldn't be there if user needed it. But we will document the
|
||||||
|
* need to clear it and suggest overriding init_reset_flags(), and give the examples.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* So we need to do 1 or 2 things - as long as we're not using Optiboot, we should force a *
|
||||||
|
* software reset if we don't see any reset flags on startup - init_reset_flags() does that, *
|
||||||
|
* Then if we're using SPM from app, we need to also flip the it that move s the interrupts *
|
||||||
|
* to the start of flash.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* INITIALIZATION FUNCTIONS LOCATED ANYWHERE SPECIAL GO HERE! *
|
||||||
|
* *
|
||||||
|
* They *MUST* be declared with both the ((naked)) ahd ((used)) attributes! Without the latter, *
|
||||||
|
* the optimizer will eliminate them. Without the former, the sketch will not start... *
|
||||||
|
* Wait what? Yeah, it was generating a and outputting a ret instruction, which caused the *
|
||||||
|
* sketch to return to nowhere under certain conditions and never reach main() at all. *
|
||||||
|
* I do not understand how the old vector fixer allowed the sketch to start ever... but *
|
||||||
|
* since it was only compiled in when flash write was enabled it could have been missed for a *
|
||||||
|
* long time. *
|
||||||
|
**************************************************************************************************/
|
||||||
|
|
||||||
|
#if (!defined(USING_OPTIBOOT))
|
||||||
|
void _initThreeStuff() __attribute__ ((naked)) __attribute__((used)) __attribute__ ((section (".init3")));
|
||||||
|
// this runs, as the name implies, before the main() function is called.
|
||||||
|
#if !defined(SPM_FROM_APP)
|
||||||
|
// If we're not doing the SPM stuff, we need only check the flags
|
||||||
|
void _initThreeStuff() {
|
||||||
|
init_reset_flags();
|
||||||
|
onPreMain();
|
||||||
|
}
|
||||||
|
#else /* Otherwise, SPM_FROM_APP is defined */
|
||||||
|
#if defined(__AVR_Dx__) /* So this better be a Dx!!! */
|
||||||
|
/*******************************************
|
||||||
|
* THIS FUNCTIONALITY IS ONLY EXPOSED ON *
|
||||||
|
* DX-SERIES PARTS SO THIS CODE CANT-HAPPEN *
|
||||||
|
* megaTinyCore. You must write to flash *
|
||||||
|
* using optiboot if required *
|
||||||
|
*******************************************/
|
||||||
|
// if we are, we also need to move the vectors. See longwinded deascription above.
|
||||||
|
void _initThreeStuff() {
|
||||||
|
init_reset_flags();
|
||||||
|
_PROTECTED_WRITE(CPUINT_CTRLA,CPUINT_IVSEL_bm);
|
||||||
|
onPreMain();
|
||||||
|
}
|
||||||
|
#if (SPM_FROM_APP == -1) /* Unlimited SPM from app and no bootloader, means we need to stick entry point on page 0 */
|
||||||
|
/* Declared as being located in .trampolines so it gets put way at the start of the binary. This guarantees that
|
||||||
|
* it will be in the first page of flash. Must be marked ((used)) or LinkTime Optimization (LTO) will see
|
||||||
|
* that nothing actually calls it and optimize it away. The trick of course is that it can be called if
|
||||||
|
* the user wants to - but it's designed to be called via hideous methods like
|
||||||
|
* __asm__ __volatile__ ("call EntryPointSPM" : "+z" (zaddress))
|
||||||
|
* see Flash.h */
|
||||||
|
/* It must be located *before everything* - including PROGMEM, which the compiler puts ahead of .init.
|
||||||
|
* .trampolines however comes before progmem. The function must be naked, it must be used, and you need to guard it
|
||||||
|
* with the rjmp that hops over the spm and ret instructions unless you jump directly to the entrypoint. */
|
||||||
|
void __spm_entrypoint (void) __attribute__ ((naked)) __attribute__((used)) __attribute__ ((section (".trampolines")));
|
||||||
|
void __spm_entrypoint (void)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"rjmp .+4" "\n\t" // Jump over this if we got here the wrong way
|
||||||
|
"EntryPointSPM:" "\n\t" // this is the label we call
|
||||||
|
"spm z+" "\n\t" // write r0, r1 to location pointed to by r30,r31 with posrincewmwr
|
||||||
|
"ret"::); // by 2, and then return.
|
||||||
|
}
|
||||||
|
#endif /* SPM from app unlimited */
|
||||||
|
#else
|
||||||
|
#error "The selected part has SPM_FROM_APP = unlimited, isn't using Optiboot, or it's not a Dx-series. You must change one of those things"
|
||||||
|
#endif /* Only AVR Dx supports this */
|
||||||
|
/* End if DXCore only spm from app stuff */
|
||||||
|
#endif
|
||||||
|
// Finally, none of these three things need to be done if running optiboot!
|
||||||
|
// We want the vectors in the alt location, it checks, clears, and stashes the reset flags (in GPR0)
|
||||||
|
// and it providews the entrypoint we call to write to flash.
|
||||||
|
#else
|
||||||
|
void _initThreeStuff() __attribute__ ((naked)) __attribute__((used)) __attribute__ ((section (".init3")));
|
||||||
|
void _initThreeStuff() {
|
||||||
|
onPreMain();
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,5 @@
|
||||||
|
/*
|
||||||
|
this header is for compatibility with standard c++ header names
|
||||||
|
so that #include<new> works as expected
|
||||||
|
*/
|
||||||
|
#include "new.h"
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 Arduino. All right reserved.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Updated 2020-21 Spence Konde to support C++ 14 sized
|
||||||
|
dealocations, and give useful compile errors when aligned
|
||||||
|
new and delete operators are referenced. I do not think adding
|
||||||
|
support for them is warranted at this time, though we have
|
||||||
|
candidates for the simplest of them, both were written by
|
||||||
|
@henrygab (Henry Gabryjelski, which is fitting because that's
|
||||||
|
who asked for C++ 17 mode, over on ATTinyCore, but releases
|
||||||
|
are more frequent for the new parts, and this is equally
|
||||||
|
applicable to all of the cores.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// For C++11, only need the following:
|
||||||
|
void *operator new (size_t size) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *operator new[](size_t size) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete (void * ptr) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete[](void * ptr) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void * operator new (size_t size, void * ptr) noexcept {
|
||||||
|
(void)size;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * operator new[](size_t size, void * ptr) noexcept {
|
||||||
|
(void)size;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since C++14, also need two more delete variants...
|
||||||
|
// Note thnat this CAN break code that compiled amd worked in C++11.
|
||||||
|
#if (__cpp_sized_deallocation >= 201309L)
|
||||||
|
void operator delete (void* ptr, size_t size) noexcept {
|
||||||
|
(void) size;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
void operator delete[](void* ptr, size_t size) noexcept {
|
||||||
|
(void) size;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Since C++17, there's four more each for new / delete, to support allocation
|
||||||
|
// of objects with alignment greater than __STDCPP_DEFAULT_NEW_ALIGNMENT__.
|
||||||
|
// Almost the entirety of AVR is using C++ 11. Any code that works elsewhere, but NOT on these cores and give these error messages either
|
||||||
|
// is trying to get alignment that it doesn't actually need, or is - by sheet luck - ending up with the addresses which worked. E
|
||||||
|
#if (__cpp_aligned_new >= 201606L)
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
void badAlloc(const char*) __attribute__((error("")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void* operator new (size_t size, std::align_val_t al) {
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
badAlloc("Alignment aware new/delete operators, a C++ 17 feaure, are not supported by this core or any other AVR cores at this point in time");
|
||||||
|
#endif
|
||||||
|
(void) al;
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void* operator new (size_t size, std::align_val_t al) {
|
||||||
|
|
||||||
|
if (al <= alignof(std::max_align_t)) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// how many extra bytes required to ensure can find an aligned pointer?
|
||||||
|
size_t toAllocate = size + (al - alignof(std::max_align_t)) - 1;
|
||||||
|
toAllocate += sizeof(uintptr_t); // to store the original pointer
|
||||||
|
|
||||||
|
uintptr_t allocated = malloc(toAllocate);
|
||||||
|
|
||||||
|
// ensure to save space for the back-pointer.
|
||||||
|
uintptr_t alignedPtr = allocated + sizeof(uintptr_t);
|
||||||
|
|
||||||
|
size_t mask = al 1; // alignment mask ... the bits that must be zero
|
||||||
|
uintptr_t alignedPtr =
|
||||||
|
(allocated & mask) == 0 ?
|
||||||
|
allocated :
|
||||||
|
(allocated & (~mask)) + al;
|
||||||
|
|
||||||
|
// save the original pointer just before the pointer value returned to caller
|
||||||
|
static_assert(alignof(uintptr_t) <= alignof(std::max_align_t), "" );
|
||||||
|
uintptr_t* storeOriginalPointerAt = (uintptr_t)(alignedPtr - sizeof(uintptr_t));
|
||||||
|
*storeOriginalPointerAt = allocated;
|
||||||
|
|
||||||
|
return alignedPtr;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void* operator new[](size_t size, std::align_val_t al) {
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
badAlloc("Alignment aware new/delete operators, a C++ 17 feaure, are not supported by this core or any other AVR cores at this point in time");
|
||||||
|
#endif
|
||||||
|
(void) al;
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
void operator delete (void* ptr, std::align_val_t al) noexcept {
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
badAlloc("Alignment aware new/delete operators, a C++ 17 feaure, are not supported by this core or any other AVR cores at this point in time");
|
||||||
|
#endif
|
||||||
|
(void) al;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
void operator delete (void* ptr, std::align_val_t al) noexcept {
|
||||||
|
|
||||||
|
if (al <= STDCPP_DEFAULT_NEW_ALIGNMENT) {
|
||||||
|
free(ptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the original pointer value from just prior to the provided pointer.
|
||||||
|
uintptr_t aligned = ptr;
|
||||||
|
uintptr_t tmp = aligned - sizeof(uintptr_t); // go back 2 bytes
|
||||||
|
uintptr_t original = *((uintptr_t *)tmp); // extract the original pointer
|
||||||
|
|
||||||
|
// sanity check the original value... for iot, infinite loop recommended
|
||||||
|
// as it's better than memory corruption!rhy
|
||||||
|
size_t overhead = (al - STDCPP_DEFAULT_NEW_ALIGNMENT) + sizeof(uintptr_t);
|
||||||
|
assert(original < aligned);
|
||||||
|
assert(original + overhead <= aligned);
|
||||||
|
|
||||||
|
// and finally free the original memory
|
||||||
|
free(original);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
void operator delete[](void* ptr, std::align_val_t al) noexcept {
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
badAlloc("Alignment aware new/delete operators, a C++ 17 feaure, are not supported by this core or any other AVR cores at this point in time");
|
||||||
|
#endif
|
||||||
|
(void) al;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
void operator delete (void* ptr, size_t size, std::align_val_t al) noexcept{
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
badAlloc("Alignment aware new/delete operators, a C++ 17 feaure, are not supported by this core or any other AVR cores at this point in time");
|
||||||
|
#endif
|
||||||
|
(void) al;
|
||||||
|
(void) size;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
void operator delete[](void* ptr, size_t size, std::align_val_t al) noexcept {
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
badAlloc("Overaligned allocation/deallocation is a C++ 17 feature, and is not supported by this or other AVR cores at this point in time");
|
||||||
|
#endif
|
||||||
|
(void) al;
|
||||||
|
(void) size;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,60 @@
|
||||||
|
/* New version (c) Spence Konde 2021
|
||||||
|
* This file is part of megaTinyCore, DxCore, and ATTinyCore.
|
||||||
|
* This is free software (released under the LGPL 2.1). For more
|
||||||
|
* information, see the License.md file included with this core.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
C++11 needed some new new and delete operators.
|
||||||
|
Since C++14, also needed two more delete variants taking a size argument.
|
||||||
|
Said size argument is ignored (cast to void), but on some architectures
|
||||||
|
it is an important way to give hints to the memory allocator for greater
|
||||||
|
efficiency.
|
||||||
|
Since C++17, there's four more each for new / delete, to support allocation
|
||||||
|
of objects with alignment greater than __STDCPP_DEFAULT_NEW_ALIGNMENT__.
|
||||||
|
This has two major differences from the C++14 sized deallocation.
|
||||||
|
1. It cannot be simply ignored - they presumably wanted alignment for
|
||||||
|
a reason; this implementation is not impossible, but nor is it trivial.
|
||||||
|
2. If code used on C++14 or earlier is asking for the alignment aware
|
||||||
|
versions of these operators, either it doesn't actually need it and
|
||||||
|
shouldn't be, or it was sheer happenstance that whatver address it
|
||||||
|
was getting worked within it's assumoptions.
|
||||||
|
Unless and until official cores or other cores in widespread use add support
|
||||||
|
for the alignment aware operators on AVRs, we have no plans to. While we
|
||||||
|
do not hesitate to offer users new features to take advantage of hardware
|
||||||
|
capabilities not present on older devices, this does not extend to new
|
||||||
|
compiler features which require modifications to code to take advantage
|
||||||
|
of. We have candidates for overaligned new and delete though, for when/if
|
||||||
|
they are required.
|
||||||
|
-Spence, 9/25/2021
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NEW_H
|
||||||
|
#define NEW_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
/* C++11 and later */
|
||||||
|
void* operator new (size_t size /* return malloc(size); */);
|
||||||
|
void* operator new[] (size_t size /* return malloc(size); */);
|
||||||
|
void* operator new (size_t size, void * ptr /* (void size); return ptr; */) noexcept;
|
||||||
|
void* operator new[] (size_t size, void * ptr /* (void size); return ptr; */) noexcept;
|
||||||
|
void operator delete (void* ptr /* free(ptr); */) noexcept;
|
||||||
|
void operator delete[] (void* ptr /* free(ptr); */) noexcept;
|
||||||
|
|
||||||
|
#if (__cpp_sized_deallocation >= 201309L) /* C++14 and later only */
|
||||||
|
void operator delete (void* ptr, size_t size /* (void)size; return ptr; */) noexcept;
|
||||||
|
void operator delete[] (void* ptr, size_t size /* (void)size; return ptr; */) noexcept;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (__cpp_aligned_new >= 201606L) /* C++17 and later only */
|
||||||
|
/* These are unsupported and just stubs that generate a more helpful error */
|
||||||
|
/* If it turns out that we do need them, we have a candidate for these */
|
||||||
|
void* operator new (size_t size, std::align_val_t al );
|
||||||
|
void operator delete (void* ptr,/* (void)al; return ptr; */std::align_val_t al) noexcept;
|
||||||
|
/* Not actually supported, these stubs just generate a more helpful error */
|
||||||
|
void* operator new[] (size_t size, std::align_val_t al );
|
||||||
|
void operator delete[](void* ptr,/* (void)al; return ptr; */std::align_val_t al) noexcept;
|
||||||
|
void operator delete (void* ptr, size_t size,/* above + */std::align_val_t al) noexcept;
|
||||||
|
void operator delete[](void* ptr, size_t size,/* (void)al;*/std::align_val_t al) noexcept;
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,343 @@
|
||||||
|
#ifndef __TIMERS_H__
|
||||||
|
#define __TIMERS_H__
|
||||||
|
#include <core_parameters.h>
|
||||||
|
|
||||||
|
/*****************************************
|
||||||
|
| # # ### # # ### ### |
|
||||||
|
| ## ## # # # # # |
|
||||||
|
| # # # # # # # ### |
|
||||||
|
| # # # # # # # |
|
||||||
|
| # # ### #### #### ### #### |
|
||||||
|
|_______________________________________*/
|
||||||
|
|
||||||
|
#ifdef MILLIS_USE_TIMERNONE
|
||||||
|
#define DISABLE_MILLIS
|
||||||
|
#endif
|
||||||
|
#if (defined(MILLIS_USE_TIMERRTC_XTAL) || defined(MILLIS_USE_TIMERRTC_XOSC))
|
||||||
|
#define MILLIS_USE_TIMERRTC
|
||||||
|
#endif
|
||||||
|
#if !defined(TCA_PORTMUX)
|
||||||
|
#define TCA_PORTMUX (0x00)
|
||||||
|
#endif
|
||||||
|
#define _TCA_USE_WO0
|
||||||
|
#define _TCA_USE_WO1
|
||||||
|
#define _TCA_USE_WO2
|
||||||
|
#if TCA_PORTMUX & 0x01
|
||||||
|
#define _TCA_ALT_WO0
|
||||||
|
#endif
|
||||||
|
#if TCA_PORTMUX & 0x02
|
||||||
|
#define _TCA_ALT_WO1
|
||||||
|
#endif
|
||||||
|
#if TCA_PORTMUX & 0x04
|
||||||
|
#define _TCA_ALT_WO2
|
||||||
|
#endif
|
||||||
|
#if !defined(TCA_BUFFERED_3PIN)
|
||||||
|
#if TCA_PORTMUX & 0x08
|
||||||
|
#define _TCA_ALT_WO3
|
||||||
|
#else
|
||||||
|
#define _TCA_USE_WO3
|
||||||
|
#endif
|
||||||
|
#if TCA_PORTMUX & 0x10
|
||||||
|
#define _TCA_ALT_WO4
|
||||||
|
#else
|
||||||
|
#define _TCA_USE_WO4
|
||||||
|
#endif
|
||||||
|
#if TCA_PORTMUX & 0x20
|
||||||
|
#define _TCA_ALT_WO5
|
||||||
|
#else
|
||||||
|
#define _TCA_USE_WO5
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(USE_TIMERD0_PWM)
|
||||||
|
#if !defined(TCD0)
|
||||||
|
#if defined(ARDUINO_MAIN)
|
||||||
|
#pragma message("Note: This part does not have a TCD, hence there is no PWM from TCD available.")
|
||||||
|
#endif
|
||||||
|
#undef USE_TIMERD0_PWM
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(MILLIS_USE_TIMERB0) || defined(MILLIS_USE_TIMERB1) || defined(MILLIS_USE_TIMERB2) || defined(MILLIS_USE_TIMERB3) || defined(MILLIS_USE_TIMERB4) || defined(MILLIS_USE_TIMERB5) || defined(MILLIS_USE_TIMERB6) || defined(MILLIS_USE_TIMERB7))
|
||||||
|
#if (F_CPU == 1000000UL)
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (1)
|
||||||
|
#define TIME_TRACKING_TIMER_PERIOD ((F_CPU/500)-1)
|
||||||
|
#elif (F_CPU == 2000000UL)
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (2)
|
||||||
|
#define TIME_TRACKING_TIMER_PERIOD ((F_CPU/1000)-1)
|
||||||
|
#else
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (2)
|
||||||
|
#define TIME_TRACKING_TIMER_PERIOD ((F_CPU/2000)-1)
|
||||||
|
#endif
|
||||||
|
#elif defined(MILLIS_USE_TIMERD0)
|
||||||
|
#define TIME_TRACKING_TIMER_PERIOD (0x1FD)
|
||||||
|
#if (F_CPU == 1000000UL)
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (64) /* Clock divider for TCD0 */
|
||||||
|
#else
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (32) /* Clock divider for TCD0 */
|
||||||
|
#endif
|
||||||
|
#else // Otherwise it must be a TCA
|
||||||
|
#define TIME_TRACKING_TIMER_PERIOD (0xFE)
|
||||||
|
#if (F_CPU > 30000000UL)
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (256)
|
||||||
|
#elif (F_CPU <= 1000000UL)
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (8)
|
||||||
|
#elif (F_CPU <= 5000000UL)
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (16)
|
||||||
|
#else // 30000000UL >= F_CPU > 5000000
|
||||||
|
#define TIME_TRACKING_TIMER_DIVIDER (64)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TIME_TRACKING_TICKS_PER_OVF (TIME_TRACKING_TIMER_PERIOD + 1UL)
|
||||||
|
#define TIME_TRACKING_CYCLES_PER_OVF (TIME_TRACKING_TICKS_PER_OVF * TIME_TRACKING_TIMER_DIVIDER)
|
||||||
|
|
||||||
|
// For a type B timer as millis, these #defines aren't needed, but they're defined accurately anyway,
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(MILLIS_USE_TIMERA0)
|
||||||
|
#ifndef TCA0
|
||||||
|
#error "TCA0, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERA0
|
||||||
|
#define MILLIS_VECTOR TCA0_HUNF_vect
|
||||||
|
#elif defined(MILLIS_USE_TIMERA1)
|
||||||
|
#ifndef TCA1
|
||||||
|
#error "TCA1, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERA1
|
||||||
|
#define MILLIS_VECTOR TCA1_HUNF_vect
|
||||||
|
#elif defined(MILLIS_USE_TIMERB0)
|
||||||
|
#ifndef TCB0
|
||||||
|
#error "TCB0, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERB0
|
||||||
|
#elif defined(MILLIS_USE_TIMERB1)
|
||||||
|
#ifndef TCB1
|
||||||
|
#error "TCB1, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERB1
|
||||||
|
#elif defined(MILLIS_USE_TIMERB2)
|
||||||
|
#ifndef TCB2
|
||||||
|
#error "TCB2, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERB2
|
||||||
|
#elif defined(MILLIS_USE_TIMERB3)
|
||||||
|
#ifndef TCB3
|
||||||
|
#error "TCB3, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERB3
|
||||||
|
#elif defined(MILLIS_USE_TIMERB4)
|
||||||
|
#ifndef TCB4
|
||||||
|
#error "TCB4, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERB4
|
||||||
|
#elif defined(MILLIS_USE_TIMERB5)
|
||||||
|
#ifndef TCB5
|
||||||
|
#error "TCB5, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERB5
|
||||||
|
#elif defined(MILLIS_USE_TIMERB6)
|
||||||
|
#ifndef TCB6
|
||||||
|
#error "TCB6, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERB6
|
||||||
|
#elif defined(MILLIS_USE_TIMERB7)
|
||||||
|
#ifndef TCB7
|
||||||
|
#error "TCB7, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERB7
|
||||||
|
#elif defined(MILLIS_USE_TIMERD0)
|
||||||
|
#ifndef TCD0
|
||||||
|
#error "TCD0, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERD0
|
||||||
|
#elif defined(MILLIS_USE_TIMERRTC)
|
||||||
|
#ifndef RTC
|
||||||
|
#error "RTC, selected for millis, does not exist on this part"
|
||||||
|
#endif
|
||||||
|
#define MILLIS_TIMER TIMERRTC
|
||||||
|
#elif defined(MILLIS_USE_TIMERNONE)
|
||||||
|
#define MILLIS_TIMER NOT_ON_TIMER
|
||||||
|
#else
|
||||||
|
#error "Millis timer not specified, nor is millis disabled - can't happen!"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* These allow other libraries to detect whether timing functions are available with #ifdef */
|
||||||
|
#define delay delay
|
||||||
|
#define delayMicroseconds delayMicroseconds
|
||||||
|
|
||||||
|
#if !defined(MILLIS_USE_TIMERNONE)
|
||||||
|
#define millis millis
|
||||||
|
#if !defined(MILLIS_USE_TIMERRTC)
|
||||||
|
#define micros micros
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************
|
||||||
|
| #### # # # # |
|
||||||
|
| # # # # ## ## |
|
||||||
|
| #### # # # # # # |
|
||||||
|
| # # # # # # |
|
||||||
|
| # # # # # |
|
||||||
|
|______________________________________*/
|
||||||
|
|
||||||
|
|
||||||
|
/* TYPE-A TIMERS */
|
||||||
|
|
||||||
|
#define PWM_TIMER_PERIOD (0xFE) // For frequency
|
||||||
|
#define PWM_TIMER_COMPARE (0x00) // For duty cycle - this is never used.
|
||||||
|
/* The original implementation set the compare registers (all 6 of them, with an STS instruction),
|
||||||
|
* and also set a number of other TCA registers to their POR values. That was dumb, and is no longer done.
|
||||||
|
* TCA0 is present on all parts and always used for PWM.
|
||||||
|
* TCA1 is used for PWM on Dx-series parts that have it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TYPE-B TIMERS */
|
||||||
|
#if defined(TCB2) && !defined(MEGATINYCORE)
|
||||||
|
// On tinyAVR, and the low pincount DD's TCB PWM isn't helpful and the timer is too valuable anyway.
|
||||||
|
// These use the same values as above for TCA, only they're only 1 channel each.
|
||||||
|
#define USE_TCB_PWM
|
||||||
|
#if defined(SUPPORT_TCB_MUX)
|
||||||
|
#undef SUPPORT_TCB_MUX
|
||||||
|
#pragma message("TCB autoMUXing is not yet supported")
|
||||||
|
#endif
|
||||||
|
/* If this feature is ever implemented, calling analogWrite() on a pin that has PWM controlled by a TCB
|
||||||
|
* it will automatically also set or clear the PORTMUX bit to point the TCB at the right place.
|
||||||
|
* These pins will be included in the pin timer table.
|
||||||
|
*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* TYPE-D TIMERS */
|
||||||
|
#if defined(MEGATINYCORE)
|
||||||
|
/* TCD0 on megaTinyCore is run from the unprescaled clock, at either 20 or 16 MHz depending on the fuse setting.
|
||||||
|
* It will always use a count prescale of 32. If the system clock is cranked all the way back to 1 MHz, though
|
||||||
|
* we knock it down by another factor of 2 because otherwise the interrupts were taking a large fraction of
|
||||||
|
* available CPU time. */
|
||||||
|
#if (defined(MILLIS_USE_TIMERD0) && F_CPU == 1000000)
|
||||||
|
#define TIMERD0_PRESCALER (TCD_CLKSEL_20MHZ_gc | TCD_CNTPRES_DIV32_gc | TCD_SYNCPRES_DIV2_gc)
|
||||||
|
// TODO: change to TIMERD0_CLOCK_SETTING here and elsewhere
|
||||||
|
// along with TIMERD0_TOP_SETTING and TIMERD0_WGMODE_SETTING
|
||||||
|
#else
|
||||||
|
#define TIMERD0_PRESCALER (TCD_CLKSEL_20MHZ_gc | TCD_CNTPRES_DIV32_gc)
|
||||||
|
#endif
|
||||||
|
// Pretty simple here!
|
||||||
|
#elif defined(DXCORE)
|
||||||
|
#if defined(TIMERD0_TOP_SETTING)
|
||||||
|
#if (TIMERD0_TOP_SETTING == 254 || TIMERD0_TOP_SETTING == 509 ||TIMERD0_TOP_SETTING == 1019 ||TIMERD0_TOP_SETTING == 2039 ||TIMERD0_TOP_SETTING == 4079 )
|
||||||
|
#if defined(TCD0)
|
||||||
|
#define USE_TIMERD0_PWM
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* On the DX-series parts, we can have a lot more fun with the Type D timer!
|
||||||
|
* This flexibility comes at the price of losing the use of TCD0 as a millis timer, but that was being done
|
||||||
|
* as a desperation move on the highly timer-constrained non-golden tinyAVR 1-series parts. The TCD0 on the
|
||||||
|
* tinyAVRs is also far less interesting - it's virtually identical as far as the timer itself, but here
|
||||||
|
* we have a much richer selection timing clock sources.
|
||||||
|
*/
|
||||||
|
#if defined(USE_TIMERD0_PWM)
|
||||||
|
#if !defined(TIMERD0_WGMODE_SETTING)
|
||||||
|
#define TIMERD0_WGMODE_SETTING (TCD_WGMODE_ONERAMP_gc) // only WGMODE_ONERAMP and WGMODE_DUALSLOPE are supported by the core, as the other options
|
||||||
|
// cannot reproduce the behavior of analogWrite as they enforce PWM being non-overlapping. If you want those modes (I think 4 ramp mode has some
|
||||||
|
// relevant applications in combination with the TCD.CTRLD options, particularly in custom single wire protocols, particularly if you need to generate events
|
||||||
|
// at certain points in the cycle.
|
||||||
|
#endif
|
||||||
|
#if !defined(TIMERD0_CLOCK_SETTING)
|
||||||
|
#if (CLOCK_SOURCE != 0)
|
||||||
|
/*
|
||||||
|
Hey, we're not using the internal oscillator for the clock! We can totally run it at an ideal frequency from internal and use external for system clock!
|
||||||
|
|
||||||
|
There are at least five ways to slice this that look nearly identical. These are virtually indistinguishable from each other giving PWM at 980 Hz.
|
||||||
|
One runs the clock at 8 MHz with /32 division on the count prescaler and /1 on the synchronizer.
|
||||||
|
Another method drops the count prescaler back to /4, and compensates by increasing the sync prescaler to /8
|
||||||
|
A third way is to drop the count prescaler back to /4, sync prescale to /1, and set the FREQSEL to 1 MHz.
|
||||||
|
Sync prescale options of /2 and /4 can be paired with setting the osc to 2 or 4 MHz as well, with the same result
|
||||||
|
Lowering count prescale dodges some bugs, and there isn't exactly a wealth of options. (it goes /4 to /32, nothing in the middle,
|
||||||
|
and no undocumented fourth option (I checked :-P).
|
||||||
|
But /32 will get you right on target (that being ~1 kHz) if CLK_OSCHF / Sync prescale = 8 MHz
|
||||||
|
And /4 will get you right on target if CLK_OSCHF / Sync prescale = 1 MHz
|
||||||
|
There are still more equivalent settings if you consider that there are more than one way to get to 1 MHz from the internal HF oscillator.
|
||||||
|
Those settings are strictly worse if you aren't using the internal osc for anything else.
|
||||||
|
On the one hand, running with clock set to 1 MHz is probably more power efficient, though nothing using a crystal is going to set any records
|
||||||
|
for power consumption - and using an external clock, well, you have the power consumption of the clock to worry about, which is often larger
|
||||||
|
than the chip itself...
|
||||||
|
On the other hand, using the /32 count prescaling exposes us to some silicon errata on the DA/DB - but in weirdo features nobody uses.
|
||||||
|
On the third hand, if the crystal or clock is defective, and you were relying on overriding clock failure callbacks with your own functions
|
||||||
|
that enable the internal oscillator at a known speed to communicate that to you (because you got a killer deal on crystals that have
|
||||||
|
astonishingly high DOA rate or something?) If clock failure is a normal occurrence, you're doing something wrong...
|
||||||
|
|
||||||
|
Anyway, regarding how we get the same frequency, well... very few Arduino folks care!
|
||||||
|
|
||||||
|
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV4_gc | TCD_SYNCPRES_DIV8_gc | TCD_CLKSEL_OSCHF_gc)
|
||||||
|
#define TIMERD0_SET_CLOCK (CLKCTRL_FREQSEL_8M_gc)
|
||||||
|
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV4_gc | TCD_SYNCPRES_DIV4_gc | TCD_CLKSEL_OSCHF_gc)
|
||||||
|
#define TIMERD0_SET_CLOCK (CLKCTRL_FREQSEL_4M_gc)
|
||||||
|
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV4_gc | TCD_SYNCPRES_DIV2_gc | TCD_CLKSEL_OSCHF_gc)
|
||||||
|
#define TIMERD0_SET_CLOCK (CLKCTRL_FREQSEL_2M_gc)
|
||||||
|
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV4_gc | TCD_SYNCPRES_DIV1_gc | TCD_CLKSEL_OSCHF_gc)
|
||||||
|
#define TIMERD0_SET_CLOCK (CLKCTRL_FREQSEL_1M_gc)
|
||||||
|
|
||||||
|
Hell... we could even set TOP to 2039 and do
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV4_gc | TCD_SYNCPRES_DIV1_gc | TCD_CLKSEL_OSCHF_gc)
|
||||||
|
#define TIMERD0_SET_CLOCK (CLKCTRL_FREQSEL_1M_gc)
|
||||||
|
#define TIMERD0_TOP_SETTING (2039)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV32_gc | TCD_SYNCPRES_DIV1_gc | TCD_CLKSEL_OSCHF_gc)
|
||||||
|
#define TIMERD0_SET_CLOCK (CLKCTRL_FREQSEL_8M_gc)
|
||||||
|
// And we can set TOP to the default 254.
|
||||||
|
#if !defined(TIMERD0_TOP_SETTING)
|
||||||
|
#define TIMERD0_TOP_SETTING (0xFE)
|
||||||
|
// that gives the target 980 kHz PWM freqwuency....
|
||||||
|
#endif
|
||||||
|
// if it's internal HF osc as system clock, it's more complicated.....
|
||||||
|
#elif (F_CPU == 5000000UL || F_CPU == 10000000UL || F_CPU == 6000000UL || F_CPU == 7000000UL || F_CPU == 14000000UL)
|
||||||
|
// These speeds are prescaled so we can run from unprescaled clock, and keep the same settings we use at higher clock.
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV32_gc | TCD_SYNCPRES_DIV1_gc | TCD_CLKSEL_OSCHF_gc)
|
||||||
|
#if !defined(TIMERD0_TOP_SETTING)
|
||||||
|
#if (F_CPU == 7000000 || F_CPU == 14000000)
|
||||||
|
#define TIMERD0_TOP_SETTING (1019)
|
||||||
|
#else
|
||||||
|
#define TIMERD0_TOP_SETTING (509)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#elif F_CPU < 4000000UL
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV4_gc | TCD_SYNCPRES_DIV1_gc | TCD_CLKSEL_CLKPER_gc)
|
||||||
|
#if !defined(TIMERD0_TOP_SETTING)
|
||||||
|
#if (F_CPU == 1000000)
|
||||||
|
#define TIMERD0_TOP_SETTING (254)
|
||||||
|
#else
|
||||||
|
#define TIMERD0_TOP_SETTING (509)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#else //catchall F_CPU speeds
|
||||||
|
#define TIMERD0_CLOCK_SETTING (TCD_CNTPRES_DIV32_gc | TCD_SYNCPRES_DIV1_gc | TCD_CLKSEL_CLKPER_gc)
|
||||||
|
#if !defined(TIMERD0_TOP_SETTING)
|
||||||
|
#if (F_CPU <= 8000000)
|
||||||
|
#define TIMERD0_TOP_SETTING (254)
|
||||||
|
#elif (F_CPU <= 20000000)
|
||||||
|
#define TIMERD0_TOP_SETTING (509)
|
||||||
|
#else
|
||||||
|
#define TIMERD0_TOP_SETTING (1019)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif // end of F_CPU tests
|
||||||
|
#else //CLOCK setting IS defined!! That is unusual indeed!
|
||||||
|
#if !defined(TIMERD0_TOP_SETTING)
|
||||||
|
#define TIMERD0_TOP_SETTING (254)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif // End of USE_TIMERD0_PWM
|
||||||
|
#endif // end of DxCore TCD stuff - see why we don't support it for millis here?
|
|
@ -0,0 +1,295 @@
|
||||||
|
/* These are the gusses at cal change needed to get these speeds untuned *
|
||||||
|
* They are concatenated with the sigrow reference, so + or - operator works.*
|
||||||
|
* Putting them all in one place allows better calculation of them as more *
|
||||||
|
* data is gathered and analyzed.
|
||||||
|
*
|
||||||
|
* Possible target frequencies supported by megaTinyCore are:
|
||||||
|
*
|
||||||
|
* 2-series, 20 MHz: 16,*20, 24, 25, 30, 32
|
||||||
|
* 16 MHz: 14,*16, 20, 24, 25, 30
|
||||||
|
* 1-series, 20 MHz: 14, 16,*20, 24, 25, 30
|
||||||
|
* 16 MHz: 12, 14,*16, 20, 24, 25
|
||||||
|
* Further, prescaled clocks derived by dividing those values by 2, 4, or 8 *
|
||||||
|
* are supported, provided that the number number of clocks per microsecond *
|
||||||
|
* is a whole number.
|
||||||
|
*
|
||||||
|
* Hence the overall list of speeds achievable is
|
||||||
|
* 2-series, 20 MHz: 1, 2, 3, 4, 5, 8, 10, 12, 16, 20, 24, 25, 30, 32 MHz
|
||||||
|
* 16 MHz: 1, 2, 3, 4, 5, 7, 8, 10, 12, 14, 16, 20, 24, 25, 30 MHz
|
||||||
|
* 1-series, 20 MHz: 1, 2, 3, 4, 5, 7, 8, 10, 12, 14, 16, 20, 24, 25, 30 MHz
|
||||||
|
* 16 MHz: 1, 2, 3, 4, 5, 7, 8, 10, 12, 14, 16, 20, 24, 25 MHz
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define GUESS_DEFAULT (-1)
|
||||||
|
|
||||||
|
#if MEGATINYCORE_SERIES == 2
|
||||||
|
#define GUESS_20_32 (62)
|
||||||
|
#define GUESS_20_30 (52)
|
||||||
|
#define GUESS_16_30 (90)
|
||||||
|
#define GUESS_20_25 (27)
|
||||||
|
#define GUESS_16_25 (59)
|
||||||
|
#define GUESS_20_24 (23)
|
||||||
|
#define GUESS_16_24 (53)
|
||||||
|
#define GUESS_20_20 (0)
|
||||||
|
#define GUESS_16_20 (27)
|
||||||
|
#define GUESS_20_16 (-23)
|
||||||
|
#define GUESS_20_14 (-1)
|
||||||
|
#define GUESS_16_16 (0)
|
||||||
|
#define GUESS_16_14 (-18)
|
||||||
|
#define MAX_TUNING (127)
|
||||||
|
#define HOME_TUNING_OFFSET (1)
|
||||||
|
#else
|
||||||
|
#define GUESS_20_30 (33)
|
||||||
|
#define GUESS_20_25 (17)
|
||||||
|
#define GUESS_20_24 (13)
|
||||||
|
#define GUESS_20_16 (-14)
|
||||||
|
#define GUESS_20_14 (-19)
|
||||||
|
#define GUESS_16_25 (35)
|
||||||
|
#define GUESS_16_24 (31)
|
||||||
|
#define GUESS_16_20 (16)
|
||||||
|
#define GUESS_16_14 (-14)
|
||||||
|
#define GUESS_16_12 (-28)
|
||||||
|
#define MAX_TUNING (63)
|
||||||
|
#define HOME_TUNING_OFFSET (2)
|
||||||
|
#endif
|
||||||
|
/* Here we pick the correct one to use for the requested frequency. *
|
||||||
|
* First TUNE_PRESCALE is determined to permit tuning to very low speeds, which is used if*
|
||||||
|
* a tuned clock is requested at a speed requiring the tuned clock to be
|
||||||
|
*ble) "
|
||||||
|
* Note the _ - outside of this file, we don't want anyone to know about this */
|
||||||
|
|
||||||
|
|
||||||
|
#if _CLOCKSPERUS > 12
|
||||||
|
#define TUNE_PRESCALE (0)
|
||||||
|
#elif _CLOCKSPERUS == 10 || _CLOCKSPERUS == 12 || _CLOCKSPERUS == 7
|
||||||
|
#define TUNE_PRESCALE (1) //enable with 2x prescale
|
||||||
|
#elif _CLOCKSPERUS == 4 || _CLOCKSPERUS == 5 || _CLOCKSPERUS == 6
|
||||||
|
#define TUNE_PRESCALE (3) //enable with 4x prescale
|
||||||
|
#elif _CLOCKSPERUS > 1 // This is the 2 (or 3) MHz case.
|
||||||
|
#define TUNE_PRESCALE (5)
|
||||||
|
#elif _CLOCKSPERUS == 1
|
||||||
|
#define TUNE_PRESCALE (7)
|
||||||
|
#else
|
||||||
|
#define TUNE_PRESCALE (0)
|
||||||
|
#endif
|
||||||
|
uint8_t GUESSCAL;
|
||||||
|
uint8_t TUNED_CALIBRATION_OFFSET;
|
||||||
|
|
||||||
|
#if MEGATINYCORE_SERIES == 2
|
||||||
|
if (_osccfg == 1) { //Don't worry, CLOCKSPERUS is constant and gets folded!
|
||||||
|
if (_CLOCKSPERUS == 32) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_32 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (5);
|
||||||
|
} else if (_CLOCKSPERUS == 30) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_30 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 25) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_25 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (3);
|
||||||
|
} else if (_CLOCKSPERUS == 24) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 20 ) {// or 5 MHz
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (1);
|
||||||
|
} else if (_CLOCKSPERUS == 16 ) {// or 8 or 4 MHz
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (0);
|
||||||
|
} else if (_CLOCKSPERUS == 12) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 10) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (1);
|
||||||
|
} else if (_CLOCKSPERUS == 8) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (0);
|
||||||
|
} else if (_CLOCKSPERUS == 6) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 5) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (1);
|
||||||
|
} else if (_CLOCKSPERUS == 4) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (0);
|
||||||
|
} else if (_CLOCKSPERUS == 2) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (0);
|
||||||
|
} else if (_CLOCKSPERUS == 1) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (0);
|
||||||
|
} else {
|
||||||
|
TUNED_CALIBRATION_OFFSET = (-1);
|
||||||
|
}
|
||||||
|
} else if(_osccfg == 0) {
|
||||||
|
if (_CLOCKSPERUS == 30) {
|
||||||
|
GUESSCAL = (-1);
|
||||||
|
TUNED_CALIBRATION_OFFSET = (-1);
|
||||||
|
} else if (_CLOCKSPERUS == 25) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_25 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (5);
|
||||||
|
} else if (_CLOCKSPERUS == 24) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 20 ) {// or 5 MHz
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_20 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (3);
|
||||||
|
} else if (_CLOCKSPERUS == 16 ) {// or 8 or 4 MHz
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 14 ) {// or 8 or 4 MHz
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_14 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (1);
|
||||||
|
} else if (_CLOCKSPERUS == 12) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (0);
|
||||||
|
} else if (_CLOCKSPERUS == 10) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_20 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 8) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 7) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_14 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 6) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 5) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 4) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 2) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 1) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else {
|
||||||
|
TUNED_CALIBRATION_OFFSET = (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* These are the gusses at cal change needed to get these speeds untuned *
|
||||||
|
* They are concatenated with the sigrow reference, so + or - operator works.*
|
||||||
|
* Putting them all in one place allows better calculation of them as more *
|
||||||
|
* data is gathered and analyzed.
|
||||||
|
*
|
||||||
|
* Possible Freq's are:
|
||||||
|
* Prescaled clock options | Tuned clock options reached directly
|
||||||
|
* 2-series, 20 MHz: 1, 2, 4, 5, 6, - 8, 10, 12 | 16,~20, 24, 25, 30, 32 -- no 14 MHz option (no room, and 14/7 MHz is a highly unwise choice of operating frequency,
|
||||||
|
* 16 MHz: 1, 2, 4, 5, 6, 7, 8, 10, 12 | 14,~16, 20, 24, 25, 30 -- no 32 MHz option, and isn't even officially supported. but what else could I put in this slot?
|
||||||
|
* 1-series, 20 MHz: 1, 2, 4, 5, 6, 7, 8, 10, 12 | 14, 16,~20, 24, 25, 30 -- no 32 MHz option, and not all parts can hit 30.
|
||||||
|
* 16 MHz: 1, 2, 4, 5, 6, 7, 8, 10, - | 12, 14,~16, 20, 24, 25 -- no 30 or 32 MHz options. Not all parts can even hit 24.10 prescaled from 20 the usual way.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#else /* 0/1-series! */
|
||||||
|
if(_osccfg == 1) {
|
||||||
|
if (_CLOCKSPERUS > 30) {
|
||||||
|
GUESSCAL = (-1);
|
||||||
|
TUNED_CALIBRATION_OFFSET = (-1);
|
||||||
|
} else if (_CLOCKSPERUS == 30) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_30 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (5);
|
||||||
|
} else if (_CLOCKSPERUS == 25) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_25 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 24) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (3);
|
||||||
|
} else if (_CLOCKSPERUS == 20) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 16) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (1);
|
||||||
|
} else if (_CLOCKSPERUS == 14) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_14 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (0);
|
||||||
|
} else if (_CLOCKSPERUS == 12) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (3);
|
||||||
|
} else if (_CLOCKSPERUS == 10) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 8) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 7) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 6) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 5) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 4) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 2) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 1) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL20M0 + GUESS_20_16 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else {
|
||||||
|
GUESSCAL = (-1);
|
||||||
|
TUNED_CALIBRATION_OFFSET = (-1);
|
||||||
|
}
|
||||||
|
} else if(_osccfg == 0) {
|
||||||
|
if (_CLOCKSPERUS >= 30) {
|
||||||
|
GUESSCAL = (-1);
|
||||||
|
TUNED_CALIBRATION_OFFSET = (-1);
|
||||||
|
} else if (_CLOCKSPERUS == 25) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_25 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (5);
|
||||||
|
} else if (_CLOCKSPERUS == 24) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_24 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 20 ) {// or 5 MHz
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_20 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (3);
|
||||||
|
} else if (_CLOCKSPERUS == 16 ) {// or 8 or 4 MHz
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 14 ) {// or 8 or 4 MHz
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_14 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (1);
|
||||||
|
} else if (_CLOCKSPERUS == 12) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_12 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (0);
|
||||||
|
} else if (_CLOCKSPERUS == 10) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_20 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 8) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 7) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_20_14 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 6) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_12 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 5) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 + GUESS_16_20 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (4);
|
||||||
|
} else if (_CLOCKSPERUS == 4) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 2) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else if (_CLOCKSPERUS == 1) {
|
||||||
|
GUESSCAL = (SIGROW_OSCCAL16M0 );
|
||||||
|
TUNED_CALIBRATION_OFFSET = (2);
|
||||||
|
} else {
|
||||||
|
GUESSCAL = (-1);
|
||||||
|
TUNED_CALIBRATION_OFFSET = (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// if no prescaling needed, remember we need to turn it off!
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,464 @@
|
||||||
|
/*
|
||||||
|
wiring_digital.c - digital input and output functions
|
||||||
|
Part of Arduino - http://www.arduino.cc/
|
||||||
|
|
||||||
|
Copyright (c) 2005-2006 David A. Mellis
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
Modified 28 September 2010 by Mark Sproul
|
||||||
|
Modified extensively 2018~2021 by Spence Konde for
|
||||||
|
megaTinyCore and DxCore.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ARDUINO_MAIN
|
||||||
|
#include "wiring_private.h"
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) void check_valid_pin_mode(uint8_t mode) {
|
||||||
|
if (__builtin_constant_p(mode)) {
|
||||||
|
if (mode != INPUT && mode != OUTPUT && mode != INPUT_PULLUP) {
|
||||||
|
badArg("The mode passed to pinMode must be INPUT, OUTPUT, or INPUT_PULLUP (these have numeric values of 0, 1, or 2); it was given a constant that was not one of these.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pinMode(uint8_t pin, uint8_t mode) {
|
||||||
|
check_valid_digital_pin(pin); /* generate compile error if a constant that is not a valid pin is used as the pin */
|
||||||
|
check_valid_pin_mode(mode); /* generate compile error if a constant that is not a valid pin mode is used as the mode */
|
||||||
|
uint8_t bit_mask = digitalPinToBitMask(pin);
|
||||||
|
if ((bit_mask == NOT_A_PIN) || (mode > INPUT_PULLUP)) {
|
||||||
|
return; /* ignore invalid pins passed at runtime */
|
||||||
|
}
|
||||||
|
volatile uint8_t * port_base = (volatile uint8_t *) (uint16_t) ((&PORTA) + digitalPinToPort(pin));
|
||||||
|
if (mode & 0x01) {
|
||||||
|
// OUTPUT mode, so write DIRSET with the mask.
|
||||||
|
*(port_base + 1) = bit_mask;
|
||||||
|
} else { // otherwise it's input so we write DIRCLR
|
||||||
|
*(port_base + 2) = bit_mask;
|
||||||
|
/* By unanimous decision of users who spoke up, we shall not set the output register to emulate classic AVRs.
|
||||||
|
if (mode == INPUT_PULLUP) {
|
||||||
|
*(port_base + 5) = bit_mask;
|
||||||
|
} else if (mode == 0) {
|
||||||
|
*(port_base + 6) = bit_mask;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
// Now an interesting question:
|
||||||
|
// Is this faster?
|
||||||
|
//
|
||||||
|
// port_base +=(uint8_t) digitalPinToBitPosition(pin)
|
||||||
|
// bit_mask = *port_base+0x10
|
||||||
|
//
|
||||||
|
// It ought to be slightly faster. After we have loaded the bit position, either we combine with 0x10 (1 clock)
|
||||||
|
// add to port base (2 clocks, even though this will never result in a carry for any valid port that exist)
|
||||||
|
// then ld (2 clocks).
|
||||||
|
// Or after loading the bit position, we can add it to port_base (2 clocks)
|
||||||
|
// then load with displacement 2 clocks. IFF the compiler puts this pointer into Y or Z, it is faster and smaller
|
||||||
|
// by 2 bytes and one clock. And let's hope it is, because the code above would suck too if it was in X.
|
||||||
|
// Handwritten assembly that exploited the knowledge that there will never be a carry would save 1 word and 1 clock.
|
||||||
|
// and could probably save at least several times that in initializing the port_base pointer. But if you're using
|
||||||
|
// pinMode you probably don't care.
|
||||||
|
port_base += (uint8_t) digitalPinToBitPosition(pin);
|
||||||
|
bit_mask = *(port_base + 0x10);
|
||||||
|
if (mode & 2) {
|
||||||
|
bit_mask |= 0x08;
|
||||||
|
} else {
|
||||||
|
bit_mask &= 0xF7;
|
||||||
|
}
|
||||||
|
*(port_base + 0x10) = bit_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This turns off PWM, if enabled. It is called automatically on every digitalWrite();
|
||||||
|
* This function can end up executing a heckovalotta code for one simple
|
||||||
|
* Note that it only operates on the PWM source with priority - TCA > TCD > TCB/DAC
|
||||||
|
* the order of the cases here doesn't matter - which one has priority is determined in
|
||||||
|
* digitalPinToTimerNow() in wiring_analog.c. That's why it's recommended to make sure
|
||||||
|
* that no pin you're about to move the PWM output of a TCA onto is currently outputting
|
||||||
|
* PWM. It can also be used from user code (unlike on the stock core). */
|
||||||
|
void turnOffPWM(uint8_t pin)
|
||||||
|
{
|
||||||
|
/* Actually turn off compare channel, not the timer */
|
||||||
|
|
||||||
|
/* Get pin's timer
|
||||||
|
* megaTinyCore only - assumes only TIMERA0, TIMERD0, or DACOUT
|
||||||
|
* can be returned here, all have only 1 bit set, so we can use
|
||||||
|
* __PeripheralControl as a mask to see if they have taken over
|
||||||
|
* any timers with minimum overhead - critical on these parts
|
||||||
|
* Since nothing that will show up here can have more than one
|
||||||
|
* one bit set, binary and will give 0x00 if that bit is cleared
|
||||||
|
* which is NOT_ON_TIMER.
|
||||||
|
*/
|
||||||
|
uint8_t digital_pin_timer = digitalPinToTimer(pin) & __PeripheralControl;
|
||||||
|
/* end megaTinyCore-specific section */
|
||||||
|
if (digital_pin_timer== NOT_ON_TIMER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t bit_mask = digitalPinToBitMask(pin);
|
||||||
|
// TCB_t *timerB;
|
||||||
|
|
||||||
|
switch (digital_pin_timer) {
|
||||||
|
|
||||||
|
/* TCA0 */
|
||||||
|
case TIMERA0:
|
||||||
|
{
|
||||||
|
#if !defined(TCA_BUFFERED_3PIN)
|
||||||
|
// uint8_t *timer_cmp_out;
|
||||||
|
/* Bit position will give output channel */
|
||||||
|
#ifdef __AVR_ATtinyxy2__ //8 pin parts
|
||||||
|
if (bit_mask == 0x80) {
|
||||||
|
bit_mask = 1; // on the xy2, WO0 is on PA7
|
||||||
|
}
|
||||||
|
if (bit_mask > 0x04) { // -> bit_pos > 2 -> output channel controlled by HCMP
|
||||||
|
bit_mask <<= 1; // mind the gap (between LCMP and HCMP)
|
||||||
|
}
|
||||||
|
#else // Normal parts
|
||||||
|
if (digitalPinToPort(pin) == PB) { // WO0-WO2, Bitmask has one of these bits 1: 0b00hhhlll.
|
||||||
|
if (bit_mask > 0x04) { // Is it one of the three high ones? If so
|
||||||
|
bit_mask <<= 1; // nudge it 1 place left swap nybbles since that's 1 clock faster than 3 rightshifts.
|
||||||
|
_SWAP(bit_mask); // swap nybbles since that's 1 clock faster than 3 rightshifts.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, it's WO3-5. These will always be on 0b00hhh000,. Here since we ARE working with a high half timer, we need to just leftshift it once.
|
||||||
|
bit_mask <<= 1;
|
||||||
|
}
|
||||||
|
#endif // End if 8 vs normal parts
|
||||||
|
TCA0.SPLIT.CTRLB &= ~bit_mask;
|
||||||
|
#else // 3-pin mode. Means we know it's either in set A or B: 0b0AAAxBBB We get 0x00ABC
|
||||||
|
if(bit_mask & 0b00111000) {
|
||||||
|
bit_mask <<= 1; // 0b01110000
|
||||||
|
} else {
|
||||||
|
//bit_maslk <<= 3; same result, but in 3 clocks 3 words instead of 1 and 1!
|
||||||
|
|
||||||
|
_SWAP(bit_mask); // 0b0111000
|
||||||
|
}
|
||||||
|
TCA0.SINGLE.CTRLB &= ~bit_mask;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't need the type b timers as this core does not use them for PWM */
|
||||||
|
|
||||||
|
// 1-series parts have a DAC that we can use...
|
||||||
|
#if defined(DAC0)
|
||||||
|
case DACOUT:
|
||||||
|
DAC0.CTRLA = 0x00;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
/* 1-series parts also have a wacky async Type D timer, but we only use it on the 20 and 24-pin parts, as it doesn't buy us anything on the 14-pin ones...
|
||||||
|
* In a future update, an option to use TCD0 for PWM on PA4 and PA5 on the 14-pin parts, with TCA0 initialized in SINGLE mode is possible, but this would
|
||||||
|
* open a can of worms regarding SINGLE mode. I think we are best off telling people to call takeOverTCA0(), takeOverTCD0() and configure it themselves
|
||||||
|
* to do what they want, because you'll never make everyone happy otherwise. My calculus would be different if they'd made 8-pin parts with more than 4k
|
||||||
|
* of flash to fit the overhead of supporting TCA0 and TCD0 with analogWrite() comfortably - that would give a pin mapping with PWM on all pins - AND
|
||||||
|
* it would be able to do buffering on the TCA pins (TCD pins can always do that).*/
|
||||||
|
#if (defined(TCD0) && defined(USE_TIMERD0_PWM))
|
||||||
|
case TIMERD0:
|
||||||
|
{
|
||||||
|
// rigmarole that produces a glitch in the PWM
|
||||||
|
#if defined(TCD_USE_WOAB)
|
||||||
|
uint8_t fc_mask = bit_mask;// Hey that works out well!
|
||||||
|
if (TCD0.FAULTCTRL & fc_mask) {
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
// uint8_t TCD0_prescaler=TCD0.CTRLA&(~TCD_ENABLE_bm);
|
||||||
|
//
|
||||||
|
TCD0.CTRLA &= ~TCD_ENABLE_bm;
|
||||||
|
_PROTECTED_WRITE(TCD0.FAULTCTRL, TCD0.FAULTCTRL & (~fc_mask));
|
||||||
|
while (!(TCD0.STATUS & TCD_ENRDY_bm)); // wait until it can be re-enabled
|
||||||
|
TCD0.CTRLA |= TCD_ENABLE_bm; // re-enable it
|
||||||
|
#if defined(NO_GLITCH_TIMERD0) /* This is enabled in all cases where TCD0 is used for PWM */
|
||||||
|
// Assuming this mode is enabled, PWM can leave the pin with INVERTED mode enabled
|
||||||
|
// So we need to make sure that's off - wouldn't that be fun to debug?
|
||||||
|
if (bit_mask == 0x10) {
|
||||||
|
PORTA.PIN4CTRL &= ~(PORT_INVEN_bm);
|
||||||
|
} else {
|
||||||
|
PORTA.PIN5CTRL &= ~(PORT_INVEN_bm);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SREG = oldSREG;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
uint8_t fc_mask = (bit_mask == 0x02 ? 0x80 : 0x40);
|
||||||
|
if (TCD0.FAULTCTRL & fc_mask) {
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
// uint8_t TCD0_prescaler=TCD0.CTRLA&(~TCD_ENABLE_bm);
|
||||||
|
//
|
||||||
|
TCD0.CTRLA &= ~TCD_ENABLE_bm;
|
||||||
|
_PROTECTED_WRITE(TCD0.FAULTCTRL, TCD0.FAULTCTRL & (~fc_mask));
|
||||||
|
while (!(TCD0.STATUS & TCD_ENRDY_bm)); // wait until it can be re-enabled
|
||||||
|
TCD0.CTRLA |= TCD_ENABLE_bm; // re-enable it
|
||||||
|
#if defined(NO_GLITCH_TIMERD0) /* This is enabled in all cases where TCD0 is used for PWM */
|
||||||
|
// Assuming this mode is enabled, PWM can leave the pin with INVERTED mode enabled
|
||||||
|
// So we need to make sure that's off - wouldn't that be fun to debug?
|
||||||
|
#if defined(USE_TCD_WOAB)
|
||||||
|
if (bit_mask == 0x01) {
|
||||||
|
PORTA.PIN4CTRL &= ~(PORT_INVEN_bm);
|
||||||
|
} else {
|
||||||
|
PORTA.PIN5CTRL &= ~(PORT_INVEN_bm);
|
||||||
|
}
|
||||||
|
#elif defined(__AVR_ATTinyxy2__)
|
||||||
|
if (bit_mask == 0x40) {
|
||||||
|
PORTA.PIN6CTRL &= ~(PORT_INVEN_bm);
|
||||||
|
} else {
|
||||||
|
PORTA.PIN7CTRL &= ~(PORT_INVEN_bm);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (bit_mask == 0x01) {
|
||||||
|
PORTC.PIN0CTRL &= ~(PORT_INVEN_bm);
|
||||||
|
} else {
|
||||||
|
PORTC.PIN1CTRL &= ~(PORT_INVEN_bm);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
SREG = oldSREG;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// END TCD0 handling
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void digitalWrite(uint8_t pin, uint8_t val) {
|
||||||
|
check_valid_digital_pin(pin);
|
||||||
|
/* Get bit mask for pin */
|
||||||
|
uint8_t bit_mask = digitalPinToBitMask(pin);
|
||||||
|
if (bit_mask == NOT_A_PIN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get port */
|
||||||
|
PORT_t *port = digitalPinToPortStruct(pin);
|
||||||
|
/* Set output to value
|
||||||
|
This now runs even if port set INPUT in order to emulate
|
||||||
|
the behavior of digitalWrite() on classic AVR devices, where
|
||||||
|
you could digitalWrite() a pin while it's an input, to ensure
|
||||||
|
that the value of the port was set correctly when it was
|
||||||
|
changed to an output. Code in the wild relies on this behavior. */
|
||||||
|
|
||||||
|
if (val == LOW) { /* If LOW */
|
||||||
|
port->OUTCLR = bit_mask;
|
||||||
|
} else if (val == CHANGE) { /* If TOGGLE
|
||||||
|
* For the pullup setting part below
|
||||||
|
* we need to know if it's been set high or low
|
||||||
|
* otherwise the pullup state could get out of
|
||||||
|
* sync with the output bit. Annoying! But we should
|
||||||
|
* have to read it before writing OUTTGL, since that can
|
||||||
|
* have a 1 clock delay. So read first + invert */
|
||||||
|
val = !(port->OUT & bit_mask);
|
||||||
|
port->OUTTGL = bit_mask;
|
||||||
|
// Now, for the pullup setting part below
|
||||||
|
// we need to know if it's been set high or low
|
||||||
|
// otherwise the pullup state could get out of
|
||||||
|
// sync with the output bit. Annoying!
|
||||||
|
val = port->OUT & bit_mask;
|
||||||
|
// val will now be 0 (LOW) if the toggling made it LOW
|
||||||
|
// or bit_mask if not. And further down, we only need to
|
||||||
|
// know if it's
|
||||||
|
/* If HIGH OR > TOGGLE */
|
||||||
|
} else {
|
||||||
|
port->OUTSET = bit_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input direction */
|
||||||
|
if (!(port->DIR & bit_mask)) {
|
||||||
|
/* Old implementation has side effect when pin set as input -
|
||||||
|
pull up is enabled if this function is called.
|
||||||
|
Should we purposely implement this side effect?
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Get bit position for getting pin ctrl reg */
|
||||||
|
uint8_t bit_pos = digitalPinToBitPosition(pin);
|
||||||
|
|
||||||
|
/* Calculate where pin control register is */
|
||||||
|
volatile uint8_t *pin_ctrl_reg = getPINnCTRLregister(port, bit_pos);
|
||||||
|
|
||||||
|
/* Save system status and disable interrupts */
|
||||||
|
uint8_t status = SREG;
|
||||||
|
cli();
|
||||||
|
|
||||||
|
if (val == LOW) {
|
||||||
|
/* Disable pullup */
|
||||||
|
*pin_ctrl_reg &= ~PORT_PULLUPEN_bm;
|
||||||
|
} else {
|
||||||
|
/* Enable pull-up */
|
||||||
|
*pin_ctrl_reg |= PORT_PULLUPEN_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore system status */
|
||||||
|
SREG = status;
|
||||||
|
}
|
||||||
|
/* Turn off PWM if applicable
|
||||||
|
* If the pin supports PWM output, we need to turn it off.
|
||||||
|
* Better to do so AFTER we have set PORTx.OUT to what we
|
||||||
|
* want it to be when we're done. The glitch would be short
|
||||||
|
* (though non-negligible since all these functions are, of
|
||||||
|
* course, slow - the worst case being a TCD pin currently
|
||||||
|
* analogWritten() 255, then digitallyWritten() to HIGH, which
|
||||||
|
* would turn it off for the time between turnOffPWM() and
|
||||||
|
* PORT->OUTCLR)
|
||||||
|
* Since there's no penalty, why make a glitch we don't have to? */
|
||||||
|
turnOffPWM(pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) void digitalWriteFast(uint8_t pin, uint8_t val) {
|
||||||
|
check_constant_pin(pin);
|
||||||
|
check_valid_digital_pin(pin);
|
||||||
|
if (pin == NOT_A_PIN) return; // sigh... I wish I didn't have to catch this... but it's all compile time known so w/e
|
||||||
|
// Mega-0, Tiny-1 style IOPORTs
|
||||||
|
// Assumes VPORTs exist starting at 0 for each PORT structure
|
||||||
|
uint8_t mask = 1 << digital_pin_to_bit_position[pin];
|
||||||
|
uint8_t port = digital_pin_to_port[pin];
|
||||||
|
VPORT_t *vport;
|
||||||
|
|
||||||
|
// Write pin value from VPORTx.OUT register
|
||||||
|
vport = (VPORT_t *)(port * 4);
|
||||||
|
/*
|
||||||
|
if (val == HIGH)
|
||||||
|
vport->OUT |= mask;
|
||||||
|
else if (val == LOW)
|
||||||
|
vport->OUT &= ~mask;
|
||||||
|
else // CHANGE
|
||||||
|
vport->IN = mask;
|
||||||
|
*/
|
||||||
|
if (val == LOW)
|
||||||
|
vport->OUT &= ~mask;
|
||||||
|
else if (val == CHANGE)
|
||||||
|
vport->IN |= mask;
|
||||||
|
else // HIGH
|
||||||
|
vport->OUT |= mask;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t digitalRead(uint8_t pin) {
|
||||||
|
check_valid_digital_pin(pin);
|
||||||
|
/* Get bit mask and check valid pin */
|
||||||
|
uint8_t bit_mask = digitalPinToBitMask(pin);
|
||||||
|
if (bit_mask == NOT_A_PIN) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Originally the Arduino core this was derived from turned off PWM on the pin
|
||||||
|
// I cannot fathom why, insofar as the Arduino team sees Arduino as an educational
|
||||||
|
// tool, and I can't think of a better way to learn about PWM...
|
||||||
|
//
|
||||||
|
// More importantkly, digitialRead() already *crawls*. Hence there is a particularly
|
||||||
|
// high bar for stuff that would make it even slower than it already is.
|
||||||
|
//
|
||||||
|
// turnOffPWM(pin);
|
||||||
|
|
||||||
|
/* Get port and check valid port */
|
||||||
|
PORT_t *port = digitalPinToPortStruct(pin);
|
||||||
|
|
||||||
|
/* Read pin value from PORTx.IN register */
|
||||||
|
if (port->IN & bit_mask) {
|
||||||
|
return HIGH;
|
||||||
|
} else {
|
||||||
|
return LOW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) int8_t digitalReadFast(uint8_t pin) {
|
||||||
|
check_constant_pin(pin);
|
||||||
|
check_valid_digital_pin(pin);
|
||||||
|
// Mega-0, Tiny-1 style IOPORTs
|
||||||
|
// Assumes VPORTs exist starting at 0 for each PORT structure
|
||||||
|
uint8_t mask = 1 << digital_pin_to_bit_position[pin];
|
||||||
|
uint8_t port = digital_pin_to_port[pin];
|
||||||
|
VPORT_t *vport;
|
||||||
|
|
||||||
|
// Old style port logic is a small integer 0 for PORTA, 1 for PORTB etc.
|
||||||
|
vport = (VPORT_t *)(port * 4);
|
||||||
|
|
||||||
|
// Read pin value from VPORTx.IN register
|
||||||
|
return !!(vport->IN & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void openDrain(uint8_t pin, uint8_t state) {
|
||||||
|
check_valid_digital_pin(pin);
|
||||||
|
uint8_t bit_mask = digitalPinToBitMask(pin);
|
||||||
|
if (bit_mask == NOT_A_PIN) return;
|
||||||
|
/* Get port */
|
||||||
|
PORT_t *port = digitalPinToPortStruct(pin);
|
||||||
|
port->OUTCLR = bit_mask;
|
||||||
|
if (state == LOW)
|
||||||
|
port->DIRSET = bit_mask;
|
||||||
|
else if (state == CHANGE)
|
||||||
|
port->DIRTGL=bit_mask;
|
||||||
|
else // assume FLOATING
|
||||||
|
port->DIRCLR=bit_mask;
|
||||||
|
turnOffPWM(pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) void openDrainFast(uint8_t pin, uint8_t val)
|
||||||
|
{
|
||||||
|
check_constant_pin(pin);
|
||||||
|
check_valid_digital_pin(pin);
|
||||||
|
if (pin == NOT_A_PIN) return; // sigh... I wish I didn't have to catch this... but it's all compile time known so w/e
|
||||||
|
// Mega-0, Tiny-1 style IOPORTs
|
||||||
|
// Assumes VPORTs exist starting at 0 for each PORT structure
|
||||||
|
uint8_t mask = 1 << digital_pin_to_bit_position[pin];
|
||||||
|
uint8_t port = digital_pin_to_port[pin];
|
||||||
|
VPORT_t *vport;
|
||||||
|
vport = (VPORT_t *)(port * 4);
|
||||||
|
PORT_t *portstr;
|
||||||
|
portstr = (PORT_t *)(0x400 + (0x20 * port));
|
||||||
|
|
||||||
|
if (val == LOW)
|
||||||
|
vport->DIR |= mask;
|
||||||
|
else if (val == CHANGE)
|
||||||
|
portstr->DIRTGL = mask;
|
||||||
|
else// FLOAT
|
||||||
|
vport->DIR &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) void pinModeFast(uint8_t pin, uint8_t mode) {
|
||||||
|
check_constant_pin(pin);
|
||||||
|
if (!__builtin_constant_p(mode)) {
|
||||||
|
badArg("mode must be constant when used with pinModeFast");
|
||||||
|
} else {
|
||||||
|
if (mode != INPUT && mode != OUTPUT && mode != INPUT_PULLUP) { //} && mode != OUTPUT_PULLUP) {
|
||||||
|
badArg("The mode passed to pinModeFast must be INPUT, OUTPUT, INPUT_PULLUP");// or OUTPUT_PULLUP");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check_valid_digital_pin(pin); // generate compile error if a constant that is not a valid pin is used as the pin
|
||||||
|
uint8_t mask = 1 << digital_pin_to_bit_position[pin];
|
||||||
|
uint8_t port = digital_pin_to_port[pin];
|
||||||
|
VPORT_t *vport;
|
||||||
|
vport = (VPORT_t *)(port * 4);
|
||||||
|
volatile uint8_t *pin_ctrl = (volatile uint8_t *) (0x410 + digital_pin_to_port[pin] * 0x20 + digital_pin_to_bit_position[pin]);
|
||||||
|
if (mode == OUTPUT)// || mode == OUTPUT_PULLUP)
|
||||||
|
vport->DIR |= mask;
|
||||||
|
else
|
||||||
|
vport->DIR &= ~mask;
|
||||||
|
// 1 clock to set direction
|
||||||
|
if (mode == INPUT_PULLUP)
|
||||||
|
*pin_ctrl |= PORT_PULLUPEN_bm;
|
||||||
|
else //SK 3/16/23 - we were doing something really dumb here. You need to turn off the pullup when set to output too!
|
||||||
|
*pin_ctrl &= ~PORT_PULLUPEN_bm;
|
||||||
|
// and 5 to switch the damned pullup.
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
/* (C) Spence Konde 2022 open source (LGPL2.1 see LICENSE.md)
|
||||||
|
* wiring_extra.cpp contains a few functions that are the sort of thing you'd expect in wiring.c or similar
|
||||||
|
* but which need to be in a cpp file because they use overloading or are just not closely related to any of
|
||||||
|
* those files' other functions.
|
||||||
|
*
|
||||||
|
*************************************************************/
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#if defined(LTODISABLED)
|
||||||
|
void badArg(__attribute__((unused))const char* c) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void badCall(__attribute__((unused))const char* c) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
void badCore(const char*) __attribute__((error("")));
|
||||||
|
#if !defined(LTODISABLED)
|
||||||
|
void checkCore(uint8_t a) {
|
||||||
|
if (!__builtin_constant_p(a)) {
|
||||||
|
badCore("If LTO is disabled, you *MUST* inform the core by defining DISABLELTO.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void _checkCore() {
|
||||||
|
checkCore(1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
void __pinconfigure(const uint8_t digital_pin, uint16_t pin_config) {
|
||||||
|
|
||||||
|
uint8_t bit_mask = digitalPinToBitMask(digital_pin);
|
||||||
|
if(bit_mask == NOT_A_PIN || !pin_config) // Return if digital pin is invalid or the other parameters or out to zero
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t bit_pos = digitalPinToBitPosition(digital_pin);
|
||||||
|
volatile uint8_t *portbase = (volatile uint8_t*) digitalPinToPortStruct(digital_pin);
|
||||||
|
|
||||||
|
// Write to selected pin direction register
|
||||||
|
uint8_t setting = pin_config & 0x03; // Mask out direction bits (DIR, DIRSET, DIRCLR, DIRTGL)
|
||||||
|
if(setting)
|
||||||
|
*(portbase + setting) = bit_mask;
|
||||||
|
|
||||||
|
// Write to selected output register
|
||||||
|
pin_config >>= 2;
|
||||||
|
setting = pin_config & 0x03;
|
||||||
|
if(setting)
|
||||||
|
*(portbase + 4 + setting) = bit_mask;
|
||||||
|
|
||||||
|
// Return if there is nothing more to configure
|
||||||
|
if(!(pin_config & 0x3FFC))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Now it gets to the fun part: The bits of PINnCTRL!
|
||||||
|
* For this we need to disable interrupts
|
||||||
|
* (for efficiency) we only read the register once
|
||||||
|
* modify that value, then write it back at the end.
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t oldSREG = SREG; // Store SREG
|
||||||
|
cli();
|
||||||
|
pin_config >>= 2;
|
||||||
|
uint8_t pinncfg = *(portbase + 0x10 + bit_pos);
|
||||||
|
// Input sense configuration (ISC)
|
||||||
|
if(pin_config & 0x08)
|
||||||
|
pinncfg = (pinncfg & 0xF8) | (pin_config & PORT_ISC_gm);
|
||||||
|
// Pullup resistor
|
||||||
|
uint8_t temp = pin_config & 0x30;
|
||||||
|
if(temp)
|
||||||
|
{
|
||||||
|
if(temp == 0x30)
|
||||||
|
pinncfg ^= PORT_PULLUPEN_bm; // Toggle pullup
|
||||||
|
else if(temp == 0x20)
|
||||||
|
pinncfg &= ~(PORT_PULLUPEN_bm); // Clear pullup
|
||||||
|
else
|
||||||
|
pinncfg |= PORT_PULLUPEN_bm; // Set pullup
|
||||||
|
}
|
||||||
|
// Invert pin
|
||||||
|
pin_config >>= 8;
|
||||||
|
#ifdef PORT_INLVL_bm
|
||||||
|
/* This feature is present on the AVR DB/DD-series, and headers suggest EA-series.
|
||||||
|
* See Ref_digital.md in the online documentation for more information.
|
||||||
|
*
|
||||||
|
* Set or clear only - toggle not supported.
|
||||||
|
* I question the usefulness of the other PINnCTRL toggles./
|
||||||
|
* I do not question the value of INLVL toggle: I am almost certain it is nil.
|
||||||
|
* If you want a toggle INLVL option, you're in an X-Y problem and are going about something
|
||||||
|
* sufficiently wrong that giving you what you think you need does you a disservice.
|
||||||
|
* If you disagree with this design decision please create an issue and explain the use case that would make this
|
||||||
|
* a worthwhile feature.
|
||||||
|
*/
|
||||||
|
temp = pin_config & 0x03;
|
||||||
|
if (temp) {
|
||||||
|
if (temp == 0x01) {
|
||||||
|
pinncfg |= 0x40; // set INLVL
|
||||||
|
} else {
|
||||||
|
pinncfg &= ~(0x40); // clear INLVL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
temp = pin_config & 0x0C;
|
||||||
|
if(temp)
|
||||||
|
{
|
||||||
|
if(temp == 0x0C)
|
||||||
|
pinncfg ^= PORT_INVEN_bm; // Toggle INVEN
|
||||||
|
else if(temp == 0x08)
|
||||||
|
pinncfg &= ~(PORT_INVEN_bm); // Clear INVEN
|
||||||
|
else
|
||||||
|
pinncfg |= PORT_INVEN_bm; // Set INVEN
|
||||||
|
}
|
||||||
|
// Write to PINnCTRL register
|
||||||
|
*(portbase + 0x10 + bit_pos) = pinncfg;
|
||||||
|
|
||||||
|
// Restore SREG
|
||||||
|
SREG = oldSREG;
|
||||||
|
}
|
||||||
|
/* Error Checking */
|
||||||
|
void _pinconfigure(uint8_t pin, uint16_t pin_config) {
|
||||||
|
check_valid_digital_pin(pin);
|
||||||
|
#if defined(HARDWIRE_INPUT_ONLY)
|
||||||
|
if (__builtin_constant_p(pin) && pin == HARDWIRE_INPUT_ONLY) {
|
||||||
|
if (__builtin_constant_p(pin_config)) {
|
||||||
|
if (pin_config & 0x01) {
|
||||||
|
badArg("This pin cannot be set as an output because of hardware constraints on the board selected and must always be INPUT only");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pin_config & 0x01) {
|
||||||
|
pin_config = pin_config & 0xFFFC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (__builtin_constant_p(pin_config)) {
|
||||||
|
if (pin_config & 0x01) {
|
||||||
|
if (pin == HARDWIRE_INPUT_ONLY){
|
||||||
|
pin_config = pin_config & 0xFFFC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pin_config & 0x01 && pin == HARDWIRE_INPUT_ONLY) {
|
||||||
|
pin_config = pin_config & 0xFFFC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
__pinconfigure(pin,pin_config);
|
||||||
|
}
|
||||||
|
/* Allow calls with manually OR'ed constants */
|
||||||
|
void pinConfigure(uint8_t digital_pin, uint16_t pin_config) {
|
||||||
|
_pinconfigure(digital_pin, pin_config);
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
wiring_private.h - Internal header file.
|
||||||
|
Part of Arduino - http://www.arduino.cc/
|
||||||
|
|
||||||
|
Copyright (c) 2005-2006 David A. Mellis
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WiringPrivate_h
|
||||||
|
#define WiringPrivate_h
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern uint8_t __PeripheralControl;
|
||||||
|
|
||||||
|
uint32_t countPulseASM(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops);
|
||||||
|
|
||||||
|
typedef void (*voidFuncPtr)(void);
|
||||||
|
|
||||||
|
#ifndef CORE_ATTACH_OLD
|
||||||
|
void __attribute__((naked)) __attribute__((noreturn)) isrBody();
|
||||||
|
#endif
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
wiring_pulse.s - pulseInASM() function in different flavours
|
||||||
|
Part of Arduino - http://www.arduino.cc/
|
||||||
|
|
||||||
|
Copyright (c) 2014 Martino Facchin, 2020 Spence Konde
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following routine was generated by avr-gcc 4.8.3 with the following parameters
|
||||||
|
* -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2
|
||||||
|
* on the original C function
|
||||||
|
*
|
||||||
|
* unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops)
|
||||||
|
* {
|
||||||
|
* unsigned long width = 0;
|
||||||
|
* // wait for any previous pulse to end
|
||||||
|
* while ((*port & bit) == stateMask)
|
||||||
|
* if (--maxloops == 0)
|
||||||
|
* return 0;
|
||||||
|
*
|
||||||
|
* // wait for the pulse to start
|
||||||
|
* while ((*port & bit) != stateMask)
|
||||||
|
* if (--maxloops == 0)
|
||||||
|
* return 0;
|
||||||
|
*
|
||||||
|
* // wait for the pulse to stop
|
||||||
|
* while ((*port & bit) == stateMask) {
|
||||||
|
* if (++width == maxloops)
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
|
* return width;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* some compiler outputs were removed but the rest of the code is untouched
|
||||||
|
*
|
||||||
|
* Spence feb 2020: not untouched anymore! The first two loops ran in 11 cycles instead
|
||||||
|
* of 16, so if no pulse was detected, the timeout would be reached when
|
||||||
|
* 11/16ths of the requested timeout had elapsed. This was fixed by the addition
|
||||||
|
* of 2 rjmps to the next line (a 2 cycle nop that uses only 1 word) and 1 nop
|
||||||
|
* to each of these loops before they decrement maxloops.
|
||||||
|
* Additionally, removed duplication of return sequence to save 12b flash
|
||||||
|
* which is conveniently exactly how much the other fix cost.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
.global countPulseASM
|
||||||
|
|
||||||
|
countPulseASM:
|
||||||
|
|
||||||
|
.LM0:
|
||||||
|
.LFBB1:
|
||||||
|
push r12 ; ; 130 pushqi1/1 [length = 1]
|
||||||
|
push r13 ; ; 131 pushqi1/1 [length = 1]
|
||||||
|
push r14 ; ; 132 pushqi1/1 [length = 1]
|
||||||
|
push r15 ; ; 133 pushqi1/1 [length = 1]
|
||||||
|
push r16 ; ; 134 pushqi1/1 [length = 1]
|
||||||
|
push r17 ; ; 135 pushqi1/1 [length = 1]
|
||||||
|
/* prologue: function */
|
||||||
|
/* frame size = 0 */
|
||||||
|
/* stack size = 6 */
|
||||||
|
.L__stack_usage = 6
|
||||||
|
mov r30,r24 ; port, port ; 2 *movhi/1 [length = 2]
|
||||||
|
mov r31,r25 ; port, port
|
||||||
|
/* unsigned long width = 0;
|
||||||
|
*** // wait for any previous pulse to end
|
||||||
|
*** while ((*port & bit) == stateMask)
|
||||||
|
*/
|
||||||
|
.LM1:
|
||||||
|
rjmp .L2 ; ; 181 jump [length = 1]
|
||||||
|
.L4:
|
||||||
|
/* if (--maxloops == 0) */
|
||||||
|
.LM2:
|
||||||
|
rjmp .LM2A ; waste an extra 5 cycles
|
||||||
|
.LM2A:
|
||||||
|
rjmp .LM2B ;
|
||||||
|
.LM2B:
|
||||||
|
nop ;
|
||||||
|
subi r16,1 ; maxloops, ; 17 addsi3/2 [length = 4]
|
||||||
|
sbc r17, r1 ; maxloops
|
||||||
|
sbc r18, r1 ; maxloops
|
||||||
|
sbc r19, r1 ; maxloops
|
||||||
|
breq .L13 ; , ; 19 branch [length = 1]
|
||||||
|
.L2:
|
||||||
|
/* if (--maxloops == 0) */
|
||||||
|
.LM3:
|
||||||
|
ld r25,Z ; D.1554, *port_7(D) ; 22 movqi_insn/4 [length = 1]
|
||||||
|
and r25,r22 ; D.1554, bit ; 24 andqi3/1 [length = 1]
|
||||||
|
cp r25,r20 ; D.1554, stateMask ; 25 *cmpqi/2 [length = 1]
|
||||||
|
breq .L4 ; , ; 26 branch [length = 1]
|
||||||
|
rjmp .L6 ; ; 184 jump [length = 1]
|
||||||
|
.L7:
|
||||||
|
/* return 0;
|
||||||
|
***
|
||||||
|
*** // wait for the pulse to start
|
||||||
|
*** while ((*port & bit) != stateMask)
|
||||||
|
*** if (--maxloops == 0)
|
||||||
|
*/
|
||||||
|
.LM4:
|
||||||
|
rjmp .LM4A ; waste an extra 5 cycles
|
||||||
|
.LM4A:
|
||||||
|
rjmp .LM4B ;
|
||||||
|
.LM4B:
|
||||||
|
nop ;
|
||||||
|
subi r16,1 ; maxloops, ; 31 addsi3/2 [length = 4]
|
||||||
|
sbc r17, r1 ; maxloops
|
||||||
|
sbc r18, r1 ; maxloops
|
||||||
|
sbc r19, r1 ; maxloops
|
||||||
|
breq .L13 ; , ; 33 branch [length = 1]
|
||||||
|
.L6:
|
||||||
|
/* if (--maxloops == 0) */
|
||||||
|
.LM5:
|
||||||
|
ld r25,Z ; D.1554, *port_7(D) ; 41 movqi_insn/4 [length = 1]
|
||||||
|
and r25,r22 ; D.1554, bit ; 43 andqi3/1 [length = 1]
|
||||||
|
cpse r25,r20 ; D.1554, stateMask ; 44 enable_interrupt-3 [length = 1]
|
||||||
|
rjmp .L7 ;
|
||||||
|
mov r12, r1 ; width ; 7 *movsi/2 [length = 4]
|
||||||
|
mov r13, r1 ; width
|
||||||
|
mov r14, r1 ; width
|
||||||
|
mov r15, r1 ; width
|
||||||
|
rjmp .L9 ; ; 186 jump [length = 1]
|
||||||
|
.L10:
|
||||||
|
/* return 0;
|
||||||
|
***
|
||||||
|
*** // wait for the pulse to stop
|
||||||
|
*** while ((*port & bit) == stateMask) {
|
||||||
|
*** if (++width == maxloops)
|
||||||
|
*/
|
||||||
|
.LM6:
|
||||||
|
ldi r24,-1 ; , ; 50 addsi3/3 [length = 5]
|
||||||
|
sub r12,r24 ; width,
|
||||||
|
sbc r13,r24 ; width,
|
||||||
|
sbc r14,r24 ; width,
|
||||||
|
sbc r15,r24 ; width,
|
||||||
|
cp r16,r12 ; maxloops, width ; 51 *cmpsi/2 [length = 4]
|
||||||
|
cpc r17,r13 ; maxloops, width
|
||||||
|
cpc r18,r14 ; maxloops, width
|
||||||
|
cpc r19,r15 ; maxloops, width
|
||||||
|
breq .L13 ; , ; 52 branch [length = 1]
|
||||||
|
.L9:
|
||||||
|
/* if (++width == maxloops) */
|
||||||
|
.LM7:
|
||||||
|
ld r24,Z ; D.1554, *port_7(D) ; 60 movqi_insn/4 [length = 1]
|
||||||
|
and r24,r22 ; D.1554, bit ; 62 andqi3/1 [length = 1]
|
||||||
|
cp r24,r20 ; D.1554, stateMask ; 63 *cmpqi/2 [length = 1]
|
||||||
|
breq .L10 ; , ; 64 branch [length = 1]
|
||||||
|
/* return 0;
|
||||||
|
*** }
|
||||||
|
*** return width;
|
||||||
|
*/
|
||||||
|
.LM8:
|
||||||
|
mov r22,r12 ; D.1553, width ; 108 movqi_insn/1 [length = 1]
|
||||||
|
mov r23,r13 ; D.1553, width ; 109 movqi_insn/1 [length = 1]
|
||||||
|
mov r24,r14 ; D.1553, width ; 110 movqi_insn/1 [length = 1]
|
||||||
|
mov r25,r15 ; D.1553, width ; 111 movqi_insn/1 [length = 1]
|
||||||
|
rjmp .LM11 ;
|
||||||
|
/* epilogue start */
|
||||||
|
.L13:
|
||||||
|
.LM10:
|
||||||
|
ldi r22,0 ; D.1553 ; 120 movqi_insn/1 [length = 1]
|
||||||
|
ldi r23,0 ; D.1553 ; 121 movqi_insn/1 [length = 1]
|
||||||
|
ldi r24,0 ; D.1553 ; 122 movqi_insn/1 [length = 1]
|
||||||
|
ldi r25,0 ; D.1553 ; 123 movqi_insn/1 [length = 1]
|
||||||
|
/* epilogue start */
|
||||||
|
.LM11:
|
||||||
|
pop r17 ; ; 138 popqi [length = 1]
|
||||||
|
pop r16 ; ; 139 popqi [length = 1]
|
||||||
|
pop r15 ; ; 140 popqi [length = 1]
|
||||||
|
pop r14 ; ; 141 popqi [length = 1]
|
||||||
|
pop r13 ; ; 142 popqi [length = 1]
|
||||||
|
pop r12 ; ; 143 popqi [length = 1]
|
||||||
|
ret ; 144 return_from_epilogue [length = 1]
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
wiring_pulse.c - pulseIn() function
|
||||||
|
Part of Arduino - http://www.arduino.cc/
|
||||||
|
|
||||||
|
Copyright (c) 2005-2006 David A. Mellis
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wiring_private.h"
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
|
||||||
|
/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH
|
||||||
|
* or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds
|
||||||
|
* to 3 minutes in length, but must be called at least a few dozen microseconds
|
||||||
|
* before the start of the pulse.
|
||||||
|
*
|
||||||
|
* This function performs better with short pulses in noInterrupt() context
|
||||||
|
*/
|
||||||
|
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||||
|
{
|
||||||
|
// cache the port and bit of the pin in order to speed up the
|
||||||
|
// pulse width measuring loop and achieve finer resolution. calling
|
||||||
|
// digitalRead() instead yields much coarser resolution.
|
||||||
|
uint8_t bit = digitalPinToBitMask(pin);
|
||||||
|
uint8_t port = digitalPinToPort(pin);
|
||||||
|
uint8_t stateMask = (state ? bit : 0);
|
||||||
|
|
||||||
|
// convert the timeout from microseconds to a number of times through
|
||||||
|
// the initial loop; it takes approximately 16 clock cycles per iteration
|
||||||
|
unsigned long maxloops = (uint32_t)microsecondsToClockCycles(timeout) / 16;
|
||||||
|
|
||||||
|
unsigned long width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops);
|
||||||
|
|
||||||
|
// prevent clockCyclesToMicroseconds to return bogus values if countPulseASM timed out
|
||||||
|
if (width) {
|
||||||
|
return clockCyclesToMicroseconds(width * 16 + 16);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !(defined(DISABLEMILLIS) || defined(MILLIS_USE_TIMERRTC)|| defined(MILLIS_USE_TIMERRT_XTAL))
|
||||||
|
/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH
|
||||||
|
* or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds
|
||||||
|
* to 3 minutes in length, but must be called at least a few dozen microseconds
|
||||||
|
* before the start of the pulse.
|
||||||
|
*
|
||||||
|
* ATTENTION:
|
||||||
|
* this function relies on micros() so cannot be used in noInterrupt() context
|
||||||
|
*/
|
||||||
|
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||||
|
{
|
||||||
|
// cache the port and bit of the pin in order to speed up the
|
||||||
|
// pulse width measuring loop and achieve finer resolution. calling
|
||||||
|
// digitalRead() instead yields much coarser resolution.
|
||||||
|
uint8_t bit = digitalPinToBitMask(pin);
|
||||||
|
uint8_t port = digitalPinToPort(pin);
|
||||||
|
uint8_t stateMask = (state ? bit : 0);
|
||||||
|
|
||||||
|
unsigned long startMicros = micros();
|
||||||
|
|
||||||
|
// wait for any previous pulse to end
|
||||||
|
while ((*portInputRegister(port) & bit) == stateMask) {
|
||||||
|
if (micros() - startMicros > timeout) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the pulse to start
|
||||||
|
while ((*portInputRegister(port) & bit) != stateMask) {
|
||||||
|
if (micros() - startMicros > timeout) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long start = micros();
|
||||||
|
// wait for the pulse to stop
|
||||||
|
while ((*portInputRegister(port) & bit) == stateMask) {
|
||||||
|
if (micros() - startMicros > timeout) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return micros() - start;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
wiring_shift.c - shiftOut() function
|
||||||
|
Part of Arduino - http://www.arduino.cc/
|
||||||
|
|
||||||
|
Copyright (c) 2005-2006 David A. Mellis
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
/*
|
||||||
|
These turn out to be slower and less flash efficient than this imlpementation.
|
||||||
|
Note that faster implementations are possible (MUCH faster, such that they might
|
||||||
|
cause compatibility problems because they're too fast) See #197
|
||||||
|
|
||||||
|
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
|
||||||
|
uint8_t value = 0;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
digitalWrite(clockPin, HIGH);
|
||||||
|
if (bitOrder == LSBFIRST)
|
||||||
|
value |= digitalRead(dataPin) << i;
|
||||||
|
else
|
||||||
|
value |= digitalRead(dataPin) << (7 - i);
|
||||||
|
digitalWrite(clockPin, LOW);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (bitOrder == LSBFIRST)
|
||||||
|
digitalWrite(dataPin, !!(val & (1 << i)));
|
||||||
|
else
|
||||||
|
digitalWrite(dataPin, !!(val & (1 << (7 - i))));
|
||||||
|
|
||||||
|
digitalWrite(clockPin, HIGH);
|
||||||
|
digitalWrite(clockPin, LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
|
||||||
|
uint8_t val = 0;
|
||||||
|
for (uint8_t i = 0; i != 8; i++) {
|
||||||
|
digitalWrite(clockPin, HIGH);
|
||||||
|
if (bitOrder == LSBFIRST) {
|
||||||
|
val = (val >> 1) | (digitalRead(dataPin) << 7);
|
||||||
|
} else {
|
||||||
|
val = (val << 1) | digitalRead(dataPin);
|
||||||
|
}
|
||||||
|
digitalWrite(clockPin, LOW);
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val) {
|
||||||
|
for (uint8_t i = 0; i != 8; i++) {
|
||||||
|
if (bitOrder == LSBFIRST) {
|
||||||
|
digitalWrite(dataPin, val & 0x01), val >>= 1;
|
||||||
|
} else {
|
||||||
|
digitalWrite(dataPin, !!(val & 0x80)), val <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
digitalWrite(clockPin, HIGH);
|
||||||
|
digitalWrite(clockPin, LOW);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
pins_arduino.h - Pin definition functions for Arduino
|
||||||
|
Part of Arduino - http://www.arduino.cc/
|
||||||
|
|
||||||
|
Copyright (c) 2007 David A. Mellis
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General
|
||||||
|
Public License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Pins_Arduino_h
|
||||||
|
#define Pins_Arduino_h
|
||||||
|
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
#define NUM_DIGITAL_PINS (6) //
|
||||||
|
#define NUM_ANALOG_INPUTS (9)
|
||||||
|
#define NUM_I2C_PINS (2) // (SDA / SCL)
|
||||||
|
#define NUM_SPI_PINS (3) // (MISO / MOSI / SCK)
|
||||||
|
#define NUM_TOTAL_FREE_PINS (NUM_DIGITAL_PINS)
|
||||||
|
#define NUM_TOTAL_PINS (6)
|
||||||
|
#define EXTERNAL_NUM_INTERRUPTS (8)
|
||||||
|
#define PINS_COUNT (6)
|
||||||
|
|
||||||
|
#define PIN_PA6 (0)
|
||||||
|
#define PIN_PA7 (1)
|
||||||
|
#define PIN_PA1 (2)
|
||||||
|
#define PIN_PA2 (3)
|
||||||
|
#define PIN_PA3 (4)
|
||||||
|
#define PIN_PA0 (5)
|
||||||
|
|
||||||
|
#define digitalPinHasPWM(p) ((p) != 0 && (p) != 5)
|
||||||
|
|
||||||
|
#ifdef DAC0
|
||||||
|
#define DAC_PIN (PIN_PA6)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LED_BUILTIN
|
||||||
|
#define LED_BUILTIN (PIN_PA3)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
#### ### #### ##### # # # # # #
|
||||||
|
# # # # # # # ## ## # # # #
|
||||||
|
#### # # #### # # # # # # #
|
||||||
|
# # # # # # # # # # # #
|
||||||
|
# ### # # # # # ### # #
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef PORTMUX_SPI0_bm
|
||||||
|
#define PIN_SPI_MISO_PINSWAP_1 (PIN_PA7)
|
||||||
|
#define PIN_SPI_SCK_PINSWAP_1 (PIN_PA3)
|
||||||
|
#define PIN_SPI_MOSI_PINSWAP_1 (PIN_PA6)
|
||||||
|
#define PIN_SPI_SS_PINSWAP_1 (PIN_PA6)
|
||||||
|
#endif
|
||||||
|
#define PIN_SPI_MISO (PIN_PA2)
|
||||||
|
#define PIN_SPI_SCK (PIN_PA3)
|
||||||
|
#define PIN_SPI_MOSI (PIN_PA1)
|
||||||
|
#define PIN_SPI_SS (PIN_PA6)
|
||||||
|
|
||||||
|
#define SPI_INTERFACES_COUNT (1)
|
||||||
|
|
||||||
|
#define PIN_WIRE_SDA (PIN_PA1)
|
||||||
|
#define PIN_WIRE_SCL (PIN_PA2)
|
||||||
|
|
||||||
|
/* Serial pin values here are not used by the core. They don't get picked up correctly in UART.h where they're needed and I can't figure out why. */
|
||||||
|
// Serial (USART0)
|
||||||
|
#define HWSERIAL0_MUX_DEFAULT (0)
|
||||||
|
#define PIN_HWSERIAL0_RX (PIN_PA7)
|
||||||
|
#define PIN_HWSERIAL0_TX (PIN_PA6)
|
||||||
|
#define PIN_HWSERIAL0_XCK (PIN_PA3)
|
||||||
|
#define PIN_HWSERIAL0_XDIR (PIN_PA0)
|
||||||
|
#define PIN_HWSERIAL0_RX_PINSWAP_1 (PIN_PA2)
|
||||||
|
#define PIN_HWSERIAL0_TX_PINSWAP_1 (PIN_PA1)
|
||||||
|
// Alt pins don't include the rarely used functions on 8-pin parts.
|
||||||
|
#define PIN_HWSERIAL0_XCK_PINSWAP_1 (NOT_A_PIN)
|
||||||
|
#define PIN_HWSERIAL0_XDIR_PINSWAP_1 (NOT_A_PIN)
|
||||||
|
|
||||||
|
/*
|
||||||
|
## # # ## # ### ### #### ### # # ###
|
||||||
|
# # ## # # # # # # # # # # ## # #
|
||||||
|
#### # # # #### # # # # ## #### # # # # ###
|
||||||
|
# # # ## # # # # # # # # # # ## #
|
||||||
|
# # # # # # #### ### ### # ### # # ###
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PIN_A0 (A0)
|
||||||
|
#define PIN_A1 (A1)
|
||||||
|
#define PIN_A2 (A2)
|
||||||
|
#define PIN_A3 (A3)
|
||||||
|
#define PIN_A6 (A6)
|
||||||
|
#define PIN_A7 (A7)
|
||||||
|
|
||||||
|
static const uint8_t A0 = PIN_PA0;
|
||||||
|
static const uint8_t A1 = PIN_PA1;
|
||||||
|
static const uint8_t A2 = PIN_PA2;
|
||||||
|
static const uint8_t A3 = PIN_PA3;
|
||||||
|
static const uint8_t A6 = PIN_PA6;
|
||||||
|
static const uint8_t A7 = PIN_PA7;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
#### ### # # ## #### #### ## # # ###
|
||||||
|
# # # ## # # # # # # # # # # # #
|
||||||
|
#### # # # # #### #### #### #### # ###
|
||||||
|
# # # ## # # # # # # # # # #
|
||||||
|
# ### # # # # # # # # # # # ###
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef ARDUINO_MAIN
|
||||||
|
|
||||||
|
// On the Arduino board, digital pins are also used
|
||||||
|
// for the analog output (software PWM). Analog input
|
||||||
|
// pins are a separate set.
|
||||||
|
|
||||||
|
// ATtiny412 / ARDUINO
|
||||||
|
// _____
|
||||||
|
// VDD 1|* |20 GND
|
||||||
|
// (DAC) (AIN6) PA6 0 2| |19 4~ PA3 (AIN3)(SCK)(EXTCLK)
|
||||||
|
// (AIN7) PA7 1 3| |18 5 PA0 (nRESET/UPDI)
|
||||||
|
// (MOSI)(TXD*)(SDA) (AIN1) PA1 2 4|_____|17 3 PA2 (AIN2)(MISO)(RXD*)(SCL)
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
|
PIN# DESC Pin Name Other/Sp ADC0 ADC1 PTC AC0 AC1 AC2 DAC0 USART0 SPI0 TWI0 TCA(PWM) TCBn TCD0 CCL
|
||||||
|
0 A2 or DAC PA6 AIN6 AIN2 X2/Y2 AINN0 AINP1 AINP0 OUT RxD WOA
|
||||||
|
1 A3 PA7 AIN7 AIN3 X3/Y3 AINP0 AINP0 AINN0 TxD *WO0 WOB LUT1-OUT
|
||||||
|
2 MOSI PA1 AIN1 *TxD MOSI SDA WO1 LUT0-IN1
|
||||||
|
3 MISO PA2 EVOUT0 AIN2 *RxD MISO SCL WO2 LUT0-IN2
|
||||||
|
4 SCK PA3 EXTCLK AIN3 *XCK SCK WO3 TCB1 WO
|
||||||
|
5 UPDI PA0 RESET/ AIN0 LUT1-IN0
|
||||||
|
UPDI
|
||||||
|
*/
|
||||||
|
|
||||||
|
const uint8_t digital_pin_to_port[] = {
|
||||||
|
PA, // 0 PA6
|
||||||
|
PA, // 1 PA7
|
||||||
|
PA, // 2 PA1
|
||||||
|
PA, // 3 PA2
|
||||||
|
PA, // 4 PA3
|
||||||
|
PA // 5 PA0
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Use this for accessing PINnCTRL register */
|
||||||
|
const uint8_t digital_pin_to_bit_position[] = {
|
||||||
|
PIN6_bp, // 0 PA6
|
||||||
|
PIN7_bp, // 1 PA7
|
||||||
|
PIN1_bp, // 2 PA1
|
||||||
|
PIN2_bp, // 3 PA2
|
||||||
|
PIN3_bp, // 4 PA3
|
||||||
|
PIN0_bp // 5 PA0
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Use this for accessing PINnCTRL register */
|
||||||
|
const uint8_t digital_pin_to_bit_mask[] = {
|
||||||
|
PIN6_bm, // 0 PA6
|
||||||
|
PIN7_bm, // 1 PA7
|
||||||
|
PIN1_bm, // 2 PA1
|
||||||
|
PIN2_bm, // 3 PA2
|
||||||
|
PIN3_bm, // 4 PA3
|
||||||
|
PIN0_bm // 5 PA0
|
||||||
|
};
|
||||||
|
//*INDENT-OFF*
|
||||||
|
const uint8_t digital_pin_to_timer[] = {
|
||||||
|
#if defined(DAC0) && !(defined(USE_TIMERD0_PWM) && defined(TCD0))
|
||||||
|
DACOUT, // 0 PA6
|
||||||
|
#elif defined(USE_TIMERD0_PWM) && defined(TCD0)
|
||||||
|
TIMERD0,
|
||||||
|
#else
|
||||||
|
NOT_ON_TIMER, // 0 PA6
|
||||||
|
#endif
|
||||||
|
#if defined(_TCA_ALT_WO0)
|
||||||
|
TIMERA0, // 1 PA7
|
||||||
|
#elif defined(USE_TIMERD0_PWM)
|
||||||
|
TIMERD0,
|
||||||
|
#endif
|
||||||
|
TIMERA0, // 2 PA1
|
||||||
|
TIMERA0, // 3 PA2
|
||||||
|
TIMERA0, // 4 PA3
|
||||||
|
NOT_ON_TIMER // 5 PA0
|
||||||
|
};
|
||||||
|
//*INDENT-ON*
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define digitalPinToAnalogInput(p) ((p<2)?(p+6):(p<5?(p-1):(p==5?0:NOT_A_PIN)))
|
||||||
|
|
||||||
|
|
||||||
|
// These serial port names are intended to allow libraries and architecture-neutral
|
||||||
|
// sketches to automatically default to the correct port name for a particular type
|
||||||
|
// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN,
|
||||||
|
// the first hardware serial port whose RX/TX pins are not dedicated to another use.
|
||||||
|
//
|
||||||
|
// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor
|
||||||
|
//
|
||||||
|
// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial
|
||||||
|
//
|
||||||
|
// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library
|
||||||
|
//
|
||||||
|
// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins.
|
||||||
|
//
|
||||||
|
// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX
|
||||||
|
// pins are NOT connected to anything by default.
|
||||||
|
#define SERIAL_PORT_MONITOR Serial
|
||||||
|
#define SERIAL_PORT_HARDWARE Serial
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Store xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="AtmelPackComponentManagement">
|
||||||
|
<ProjectComponents>
|
||||||
|
<ProjectComponent z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
|
||||||
|
<CApiVersion></CApiVersion>
|
||||||
|
<CBundle></CBundle>
|
||||||
|
<CClass>Device</CClass>
|
||||||
|
<CGroup>Startup</CGroup>
|
||||||
|
<CSub></CSub>
|
||||||
|
<CVariant></CVariant>
|
||||||
|
<CVendor>Atmel</CVendor>
|
||||||
|
<CVersion>1.10.0</CVersion>
|
||||||
|
<DefaultRepoPath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs</DefaultRepoPath>
|
||||||
|
<DependentComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
|
||||||
|
<Description></Description>
|
||||||
|
<Files xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\include\</AbsolutePath>
|
||||||
|
<Attribute></Attribute>
|
||||||
|
<Category>include</Category>
|
||||||
|
<Condition>C</Condition>
|
||||||
|
<FileContentHash i:nil="true" />
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>include/</Name>
|
||||||
|
<SelectString></SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\include\avr\iotn402.h</AbsolutePath>
|
||||||
|
<Attribute></Attribute>
|
||||||
|
<Category>header</Category>
|
||||||
|
<Condition>C</Condition>
|
||||||
|
<FileContentHash>g0XngzH9uwBI0kp8K6DswA==</FileContentHash>
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>include/avr/iotn402.h</Name>
|
||||||
|
<SelectString></SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\templates\main.c</AbsolutePath>
|
||||||
|
<Attribute>template</Attribute>
|
||||||
|
<Category>source</Category>
|
||||||
|
<Condition>C Exe</Condition>
|
||||||
|
<FileContentHash>KjvOcFWd++tbnsEMfVPd/w==</FileContentHash>
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>templates/main.c</Name>
|
||||||
|
<SelectString>Main file (.c)</SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\templates\main.cpp</AbsolutePath>
|
||||||
|
<Attribute>template</Attribute>
|
||||||
|
<Category>source</Category>
|
||||||
|
<Condition>C Exe</Condition>
|
||||||
|
<FileContentHash>uK6xwpZG+RPPFiJUJnueCQ==</FileContentHash>
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>templates/main.cpp</Name>
|
||||||
|
<SelectString>Main file (.cpp)</SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
<d4p1:anyType i:type="FileInfo">
|
||||||
|
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.10.348\gcc\dev\attiny402</AbsolutePath>
|
||||||
|
<Attribute></Attribute>
|
||||||
|
<Category>libraryPrefix</Category>
|
||||||
|
<Condition>GCC</Condition>
|
||||||
|
<FileContentHash i:nil="true" />
|
||||||
|
<FileVersion></FileVersion>
|
||||||
|
<Name>gcc/dev/attiny402</Name>
|
||||||
|
<SelectString></SelectString>
|
||||||
|
<SourcePath></SourcePath>
|
||||||
|
</d4p1:anyType>
|
||||||
|
</Files>
|
||||||
|
<PackName>ATtiny_DFP</PackName>
|
||||||
|
<PackPath>C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATtiny_DFP/1.10.348/Atmel.ATtiny_DFP.pdsc</PackPath>
|
||||||
|
<PackVersion>1.10.348</PackVersion>
|
||||||
|
<PresentInProject>true</PresentInProject>
|
||||||
|
<ReferenceConditionId>ATtiny402</ReferenceConditionId>
|
||||||
|
<RteComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
|
||||||
|
<d4p1:string></d4p1:string>
|
||||||
|
</RteComponents>
|
||||||
|
<Status>Resolved</Status>
|
||||||
|
<VersionMode>Fixed</VersionMode>
|
||||||
|
<IsComponentInAtProject>true</IsComponentInAtProject>
|
||||||
|
</ProjectComponent>
|
||||||
|
</ProjectComponents>
|
||||||
|
</Store>
|
|
@ -0,0 +1,226 @@
|
||||||
|
<?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>ATtiny402</avrdevice>
|
||||||
|
<avrdeviceseries>none</avrdeviceseries>
|
||||||
|
<OutputType>Executable</OutputType>
|
||||||
|
<Language>CPP</Language>
|
||||||
|
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
|
||||||
|
<OutputFileExtension>.elf</OutputFileExtension>
|
||||||
|
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
|
||||||
|
<AssemblyName>HackSpaceCon_AS7</AssemblyName>
|
||||||
|
<Name>HSC_Wand</Name>
|
||||||
|
<RootNamespace>HackSpaceCon_AS7</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>
|
||||||
|
<ResetRule>0</ResetRule>
|
||||||
|
<eraseonlaunchrule>0</eraseonlaunchrule>
|
||||||
|
<EraseKey />
|
||||||
|
<AsfFrameworkConfig>
|
||||||
|
<framework-data xmlns="">
|
||||||
|
<options />
|
||||||
|
<configurations />
|
||||||
|
<files />
|
||||||
|
<documentation help="" />
|
||||||
|
<offline-documentation help="" />
|
||||||
|
<dependencies>
|
||||||
|
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.52.0" />
|
||||||
|
</dependencies>
|
||||||
|
</framework-data>
|
||||||
|
</AsfFrameworkConfig>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<ToolchainSettings>
|
||||||
|
<AvrGccCpp>
|
||||||
|
<avrgcc.common.Device>-mmcu=attiny402 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\gcc\dev\attiny402"</avrgcc.common.Device>
|
||||||
|
<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\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
<Value>..\include\libraries\EEPROM\src</Value>
|
||||||
|
<Value>..\include\libraries\Event\src</Value>
|
||||||
|
<Value>..\include\libraries\Logic\src</Value>
|
||||||
|
<Value>..\include\libraries\megaTinyCore\examples\TuningSource</Value>
|
||||||
|
<Value>..\include\libraries\megaTinyCore\src</Value>
|
||||||
|
<Value>..\include\libraries\SoftwareSerial\src</Value>
|
||||||
|
<Value>..\include\libraries\SPI\src</Value>
|
||||||
|
<Value>..\include\libraries\tinyNeoPixel</Value>
|
||||||
|
<Value>..\include\libraries\tinyNeoPixel_Static</Value>
|
||||||
|
<Value>..\include\libraries\Wire\src</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\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
<Value>..\include\libraries\EEPROM\src</Value>
|
||||||
|
<Value>..\include\libraries\Event\src</Value>
|
||||||
|
<Value>..\include\libraries\Logic\src</Value>
|
||||||
|
<Value>..\include\libraries\megaTinyCore\examples\TuningSource</Value>
|
||||||
|
<Value>..\include\libraries\megaTinyCore\src</Value>
|
||||||
|
<Value>..\include\libraries\SoftwareSerial\src</Value>
|
||||||
|
<Value>..\include\libraries\SPI\src</Value>
|
||||||
|
<Value>..\include\libraries\tinyNeoPixel</Value>
|
||||||
|
<Value>..\include\libraries\tinyNeoPixel_Static</Value>
|
||||||
|
<Value>..\include\libraries\Wire\src</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.linker.libraries.Libraries>
|
||||||
|
<ListValues>
|
||||||
|
<Value>libm</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.linker.libraries.Libraries>
|
||||||
|
<avrgcccpp.assembler.general.IncludePaths>
|
||||||
|
<ListValues>
|
||||||
|
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.assembler.general.IncludePaths>
|
||||||
|
</AvrGccCpp>
|
||||||
|
</ToolchainSettings>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<ToolchainSettings>
|
||||||
|
<AvrGccCpp>
|
||||||
|
<avrgcc.common.Device>-mmcu=attiny402 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\gcc\dev\attiny402"</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>
|
||||||
|
<Value>ARDUINO=108019</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcc.compiler.symbols.DefSymbols>
|
||||||
|
<avrgcc.compiler.directories.IncludePaths>
|
||||||
|
<ListValues>
|
||||||
|
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
<Value>../../Arduino/megatinycore</Value>
|
||||||
|
<Value>../../Arduino/variants/txy2</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.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
|
||||||
|
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
|
||||||
|
<avrgcc.compiler.miscellaneous.OtherFlags>-std=gnu11</avrgcc.compiler.miscellaneous.OtherFlags>
|
||||||
|
<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>
|
||||||
|
<Value>ARDUINO=108019</Value>
|
||||||
|
<Value>ARDUINO_AVR_ATtinyxy4</Value>
|
||||||
|
<Value>ARDUINO_attinyxy4</Value>
|
||||||
|
<Value>F_CPU=8000000UL</Value>
|
||||||
|
<Value>MEGATINYCORE="2.6.10"</Value>
|
||||||
|
<Value>MEGATINYCORE_MAJOR=2UL</Value>
|
||||||
|
<Value>MEGATINYCORE_MINOR=6UL</Value>
|
||||||
|
<Value>MEGATINYCORE_PATCH=10UL</Value>
|
||||||
|
<Value>MEGATINYCORE_RELEASED=1</Value>
|
||||||
|
<Value>MILLIS_USE_TIMERA0</Value>
|
||||||
|
<Value>CLOCK_SOURCE=0</Value>
|
||||||
|
<Value>TWI_MORS</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.compiler.symbols.DefSymbols>
|
||||||
|
<avrgcccpp.compiler.directories.IncludePaths>
|
||||||
|
<ListValues>
|
||||||
|
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
<Value>../../Arduino/megatinycore</Value>
|
||||||
|
<Value>../../Arduino/variants/txy2</Value>
|
||||||
|
<Value>../../Arduino/arduino_libraries/tinyNeoPixel_static</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.compiler.directories.IncludePaths>
|
||||||
|
<avrgcccpp.compiler.optimization.level>Optimize for size (-Os)</avrgcccpp.compiler.optimization.level>
|
||||||
|
<avrgcccpp.compiler.optimization.OtherFlags>-flto -fno-threadsafe-statics</avrgcccpp.compiler.optimization.OtherFlags>
|
||||||
|
<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.InhibitAllWarnings>True</avrgcccpp.compiler.warnings.InhibitAllWarnings>
|
||||||
|
<avrgcccpp.compiler.miscellaneous.OtherFlags>-std=gnu++17</avrgcccpp.compiler.miscellaneous.OtherFlags>
|
||||||
|
<avrgcccpp.linker.libraries.Libraries>
|
||||||
|
<ListValues>
|
||||||
|
<Value>libm</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.linker.libraries.Libraries>
|
||||||
|
<avrgcccpp.linker.miscellaneous.LinkerFlags>-Os -fuse-linker-plugin</avrgcccpp.linker.miscellaneous.LinkerFlags>
|
||||||
|
<avrgcccpp.assembler.general.IncludePaths>
|
||||||
|
<ListValues>
|
||||||
|
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.10.348\include\</Value>
|
||||||
|
</ListValues>
|
||||||
|
</avrgcccpp.assembler.general.IncludePaths>
|
||||||
|
<avrgcccpp.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcccpp.assembler.debugging.DebugLevel>
|
||||||
|
</AvrGccCpp>
|
||||||
|
</ToolchainSettings>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="hsv2rgb.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="hsv2rgb.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="isr.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="wand_program.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="rgbled.cpp">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="rgbled.h">
|
||||||
|
<SubType>compile</SubType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Arduino\Arduino.cppproj">
|
||||||
|
<Name>Arduino</Name>
|
||||||
|
<Project>{47694d30-4990-4f03-a1f7-e94eb79783f0}</Project>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 B. Stultiens
|
||||||
|
* modified by true for 12-bit values
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hsv2rgb.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
void hsv2rgb_8b(uint16_t h, uint16_t s, uint16_t v, uint16_t *r, uint16_t *g , uint16_t *b)
|
||||||
|
{
|
||||||
|
uint8_t sextant;
|
||||||
|
uint8_t bb;
|
||||||
|
uint16_t ww;
|
||||||
|
uint8_t h_fraction;
|
||||||
|
|
||||||
|
if (!(s)) {
|
||||||
|
*(r) = *(g) = *(b) = (v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sextant = h >> 8;
|
||||||
|
HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space
|
||||||
|
|
||||||
|
HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in
|
||||||
|
|
||||||
|
*g = v; // Top level
|
||||||
|
|
||||||
|
// Perform actual calculations
|
||||||
|
|
||||||
|
// Bottom level: v * (1.0 - s)
|
||||||
|
// --> (v * (255 - s) + error_corr) / 256
|
||||||
|
|
||||||
|
bb = ~s;
|
||||||
|
ww = v * bb;
|
||||||
|
ww += 1; // Error correction
|
||||||
|
ww += ww >> 8; // Error correction
|
||||||
|
*b = ww >> 8;
|
||||||
|
|
||||||
|
h_fraction = h & 0xff; // 0...255
|
||||||
|
|
||||||
|
if(!(sextant & 1)) {
|
||||||
|
// *r = ...slope_up...;
|
||||||
|
|
||||||
|
// Slope up: v * (1.0 - s * (1.0 - h))
|
||||||
|
// --> (v * (255 - (s * (256 - h) + error_corr1) / 256) + error_corr2) / 256
|
||||||
|
|
||||||
|
ww = !h_fraction ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_fraction));
|
||||||
|
ww += ww >> 8; // Error correction 1
|
||||||
|
bb = ww >> 8;
|
||||||
|
bb = ~bb;
|
||||||
|
ww = v * bb;
|
||||||
|
ww += v >> 1; // Error correction 2
|
||||||
|
*r = ww >> 8;
|
||||||
|
} else {
|
||||||
|
// *r = ...slope_down...;
|
||||||
|
|
||||||
|
// Slope down: v * (1.0 - s * h)
|
||||||
|
// --> (v * (255 - (s * h + error_corr1) / 256) + error_corr2) / 256
|
||||||
|
|
||||||
|
ww = s * h_fraction;
|
||||||
|
ww += ww >> 8; // Error correction 1
|
||||||
|
bb = ww >> 8;
|
||||||
|
bb = ~bb;
|
||||||
|
ww = v * bb;
|
||||||
|
ww += v >> 1; // Error correction 2
|
||||||
|
*r = ww >> 8;
|
||||||
|
|
||||||
|
// A perfect match for h_fraction == 0 implies:
|
||||||
|
// *r = (ww >> 8) + (h_fraction ? 0 : 1)
|
||||||
|
// However, this is an extra calculation that may not be required.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void hsv2rgb_32b_8(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b)
|
||||||
|
{
|
||||||
|
HSV_MONOCHROMATIC_TEST(s, v, r, g, b); // Exit with grayscale if s == 0
|
||||||
|
|
||||||
|
uint8_t sextant = h >> 8;
|
||||||
|
|
||||||
|
HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space
|
||||||
|
|
||||||
|
HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in
|
||||||
|
|
||||||
|
*g = v; // Top level
|
||||||
|
|
||||||
|
// Perform actual calculations
|
||||||
|
|
||||||
|
// Bottom level: v * (1.0 - s)
|
||||||
|
// --> (v * (255 - s) + error_corr + 1) / 256
|
||||||
|
|
||||||
|
uint16_t ww; // Intermediate result
|
||||||
|
ww = v * (255 - s); // We don't use ~s to prevent size-promotion side effects
|
||||||
|
ww += 1; // Error correction
|
||||||
|
ww += ww >> 8; // Error correction
|
||||||
|
*b = ww >> 8;
|
||||||
|
|
||||||
|
uint8_t h_fraction = h & 0xff; // 0...255
|
||||||
|
uint32_t d; // Intermediate result
|
||||||
|
|
||||||
|
if(!(sextant & 1)) {
|
||||||
|
// *r = ...slope_up...;
|
||||||
|
d = v * (uint32_t)((255 << 8) - (uint16_t)(s * (256 - h_fraction)));
|
||||||
|
d += d >> 8; // Error correction
|
||||||
|
d += v; // Error correction
|
||||||
|
*r = d >> 16;
|
||||||
|
} else {
|
||||||
|
// *r = ...slope_down...;
|
||||||
|
d = v * (uint32_t)((255 << 8) - (uint16_t)(s * h_fraction));
|
||||||
|
d += d >> 8; // Error correction
|
||||||
|
d += v; // Error correction
|
||||||
|
*r = d >> 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void hsv2rgb_32b_16(uint16_t h, uint16_t s, uint16_t v, uint16_t *r, uint16_t *g , uint16_t *b)
|
||||||
|
{
|
||||||
|
HSV_MONOCHROMATIC_TEST(s, v, r, g, b); // Exit with grayscale if s == 0
|
||||||
|
|
||||||
|
uint8_t sextant = h >> 8;
|
||||||
|
|
||||||
|
HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space
|
||||||
|
|
||||||
|
HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in
|
||||||
|
|
||||||
|
*g = v; // Top level
|
||||||
|
|
||||||
|
// Bottom level: v * (1.0 - s)
|
||||||
|
// --> (v * (255 - s) + error_corr + 1) / 256
|
||||||
|
|
||||||
|
uint16_t ww; // Intermediate result
|
||||||
|
ww = v * (255 - s); // We don't use ~s to prevent size-promotion side effects
|
||||||
|
ww += 1; // Error correction
|
||||||
|
ww += ww >> 8; // Error correction
|
||||||
|
*b = ww >> 8;
|
||||||
|
|
||||||
|
uint8_t h_fraction = h & 0xff; // 0...255
|
||||||
|
uint32_t d; // Intermediate result
|
||||||
|
|
||||||
|
if(!(sextant & 1)) {
|
||||||
|
// *r = ...slope_up...;
|
||||||
|
d = v * (uint32_t)((255 << 8) - (uint16_t)(s * (256 - h_fraction)));
|
||||||
|
d += d >> 8; // Error correction
|
||||||
|
d += v; // Error correction
|
||||||
|
*r = d >> 16;
|
||||||
|
} else {
|
||||||
|
// *r = ...slope_down...;
|
||||||
|
d = v * (uint32_t)((255 << 8) - (uint16_t)(s * h_fraction));
|
||||||
|
d += d >> 8; // Error correction
|
||||||
|
d += v; // Error correction
|
||||||
|
*r = d >> 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 B. Stultiens
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef __HSV2RGB_H__
|
||||||
|
#define __HSV2RGB_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct color_rgb {
|
||||||
|
uint16_t r;
|
||||||
|
uint16_t g;
|
||||||
|
uint16_t b;
|
||||||
|
} color_rgb;
|
||||||
|
|
||||||
|
typedef struct color_hsv {
|
||||||
|
uint16_t h;
|
||||||
|
uint16_t s;
|
||||||
|
uint16_t v;
|
||||||
|
} color_hsv;
|
||||||
|
|
||||||
|
|
||||||
|
#define HSV2RGB_BITS 8 // 10
|
||||||
|
#define HSV2RGB_COUNT ((1 < HSV2RGB_BITS)-1)
|
||||||
|
|
||||||
|
#define HSV_HUE_SEXTANT (1 << HSV2RGB_BITS)
|
||||||
|
#define HSV_HUE_STEPS (6 * HSV_HUE_SEXTANT)
|
||||||
|
|
||||||
|
#define HSV_HUE_MIN 0
|
||||||
|
#define HSV_HUE_MAX (HSV_HUE_STEPS - 1)
|
||||||
|
#define HSV_SAT_MIN 0
|
||||||
|
#define HSV_SAT_MAX 255
|
||||||
|
#define HSV_VAL_MIN 0
|
||||||
|
#define HSV_VAL_MAX 255
|
||||||
|
|
||||||
|
/* Options: */
|
||||||
|
#define HSV_USE_SEXTANT_TEST /* Limit the hue to 0...360 degrees */
|
||||||
|
|
||||||
|
#define hsv2rgb(h,s,v,r,g,b) hsv2rgb_32b_8(h,s,v,r,g,b)
|
||||||
|
|
||||||
|
|
||||||
|
//void hsv2rgb_8b(uint16_t h, uint16_t s, uint16_t v, uint16_t *r, uint16_t *g , uint16_t *b);
|
||||||
|
void hsv2rgb_32b_8(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b);
|
||||||
|
//void hsv2rgb_32b_16(uint16_t h, uint16_t s, uint16_t v, uint16_t *r, uint16_t *g , uint16_t *b);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Macros that are common to all implementations
|
||||||
|
*/
|
||||||
|
#ifdef HSV_USE_SEXTANT_TEST
|
||||||
|
#define HSV_SEXTANT_TEST(sextant) \
|
||||||
|
if((sextant) > 5) { \
|
||||||
|
(sextant) = 5; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define HSV_SEXTANT_TEST(sextant)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pointer swapping:
|
||||||
|
* sext. r g b r<>b g<>b r <> g result
|
||||||
|
* 0 0 0 v u c !u v c u v c
|
||||||
|
* 0 0 1 d v c d v c
|
||||||
|
* 0 1 0 c v u u v c u v c
|
||||||
|
* 0 1 1 c d v v d c d v c d v c
|
||||||
|
* 1 0 0 u c v u v c u v c
|
||||||
|
* 1 0 1 v c d v d c d v c d v c
|
||||||
|
*
|
||||||
|
* if(sextant & 2)
|
||||||
|
* r <-> b
|
||||||
|
*
|
||||||
|
* if(sextant & 4)
|
||||||
|
* g <-> b
|
||||||
|
*
|
||||||
|
* if(!(sextant & 6) {
|
||||||
|
* if(!(sextant & 1))
|
||||||
|
* r <-> g
|
||||||
|
* } else {
|
||||||
|
* if(sextant & 1)
|
||||||
|
* r <-> g
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
#define HSV_SWAPPTR(a,b) do { uint8_t *tmp = (a); (a) = (b); (b) = tmp; } while(0)
|
||||||
|
//#define HSV_SWAPPTR(a,b) do { uint16_t *tmp = (a); (a) = (b); (b) = tmp; } while(0)
|
||||||
|
#define HSV_POINTER_SWAP(sextant,r,g,b) \
|
||||||
|
do { \
|
||||||
|
if((sextant) & 2) { \
|
||||||
|
HSV_SWAPPTR((r), (b)); \
|
||||||
|
} \
|
||||||
|
if((sextant) & 4) { \
|
||||||
|
HSV_SWAPPTR((g), (b)); \
|
||||||
|
} \
|
||||||
|
if(!((sextant) & 6)) { \
|
||||||
|
if(!((sextant) & 1)) { \
|
||||||
|
HSV_SWAPPTR((r), (g)); \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
if((sextant) & 1) { \
|
||||||
|
HSV_SWAPPTR((r), (g)); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
#define HSV_MONOCHROMATIC_TEST(s,v,r,g,b) \
|
||||||
|
do { \
|
||||||
|
if(!(s)) { \
|
||||||
|
*(r) = *(g) = *(b) = (v); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
#endif
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
hackspacecon wand firmware
|
||||||
|
|
||||||
|
interrupt routines
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "rgbled.h"
|
||||||
|
|
||||||
|
extern uint8_t run_rgbprog;
|
||||||
|
|
||||||
|
|
||||||
|
// TCB0 general interrupt
|
||||||
|
ISR(TCB0_INT_vect)
|
||||||
|
{
|
||||||
|
uint8_t intflags = TCB0.INTFLAGS;
|
||||||
|
|
||||||
|
// in this program, this interrupt is only used for timing.
|
||||||
|
// we'll now return to executing loop()
|
||||||
|
|
||||||
|
// reset the INTFLAGS - necessary on this series
|
||||||
|
TCB0.INTFLAGS = intflags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// button interrupt
|
||||||
|
ISR(PORTA_PORT_vect)
|
||||||
|
{
|
||||||
|
uint8_t intflags = PORTA.INTFLAGS;
|
||||||
|
|
||||||
|
// shitty debounce; this is bad practice
|
||||||
|
delay(5);
|
||||||
|
|
||||||
|
// was our pin changed?
|
||||||
|
if (intflags & PIN3_bm) {
|
||||||
|
// start or re-start running a program
|
||||||
|
run_rgbprog = 1; // run a new program
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the INTFLAGS - necessary on this series
|
||||||
|
PORTA.INTFLAGS = intflags;
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
hackspacecon wand firmware
|
||||||
|
|
||||||
|
rgb led control and programs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "rgbled.h"
|
||||||
|
|
||||||
|
#include "hsv2rgb.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
tinyNeoPixel rgb = tinyNeoPixel(RGB_COUNT, PIN_PA1, NEO_GRB, rgbled);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// rgb program prototypes
|
||||||
|
uint8_t rgbp_rainbow(uint8_t init);
|
||||||
|
uint8_t rgbp_circlefade(uint8_t init);
|
||||||
|
|
||||||
|
// rgb program function pointer array
|
||||||
|
uint8_t (*rgb_program[PROG_COUNT])(uint8_t) = {
|
||||||
|
rgbp_rainbow,
|
||||||
|
rgbp_circlefade
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t rgbled[3 * RGB_COUNT];
|
||||||
|
|
||||||
|
|
||||||
|
// configures and enables the 50Hz timer interrupt that is used for RGB program updates
|
||||||
|
void conf_rgb_timer()
|
||||||
|
{
|
||||||
|
// this timer will run at half speed.
|
||||||
|
// so 8MHz / 2 (prescale) / 1 (CLK_PER) = 4MHz
|
||||||
|
// this will allow a full cycle time of ~61Hz.
|
||||||
|
|
||||||
|
_PROTECTED_WRITE(CLKCTRL_MCLKCTRLB, 0); // disable CLK_PER divider
|
||||||
|
|
||||||
|
disable_rgb_timer();
|
||||||
|
|
||||||
|
TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc; // prescale timer to run at half speed
|
||||||
|
TCB0.CCMP = 0xffff; // count to full
|
||||||
|
TCB0.CNT = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// globals for all rgb programs
|
||||||
|
uint16_t prog_timeout;
|
||||||
|
uint16_t hue;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
|
||||||
|
|
||||||
|
// rgb program 0: rainbow puke
|
||||||
|
#define RAINBOW_HUE_INC 40 // how much to increment the hue every frame
|
||||||
|
#define RAINBOW_OFFSET (1536/5) // offset between each LED
|
||||||
|
#define RAINBOW_TIMEOUT 240 // how long to show this program
|
||||||
|
#define RAINBOW_SAT 0xff // saturation
|
||||||
|
#define RAINBOW_VAL 0x40 // value (brightness); keep low enough to keep average current down
|
||||||
|
|
||||||
|
uint16_t rainbow_hue = 0;
|
||||||
|
|
||||||
|
uint8_t rgbp_rainbow(uint8_t init)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
// set our timer when initializing. otherwise every call is identical
|
||||||
|
if (init) {
|
||||||
|
prog_timeout = RAINBOW_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--prog_timeout) {
|
||||||
|
// copy stored hue to working hue
|
||||||
|
hue = rainbow_hue;
|
||||||
|
|
||||||
|
for (i = 0; i < RGB_COUNT; i++) {
|
||||||
|
// each LED will increment its hue
|
||||||
|
hue += RAINBOW_OFFSET;
|
||||||
|
|
||||||
|
// hue wheel is 256*6 large, so bound the value
|
||||||
|
if (hue >= 1536) hue -= 1536;
|
||||||
|
|
||||||
|
// compute rgb from hue/sat/value
|
||||||
|
hsv2rgb(hue, RAINBOW_SAT, RAINBOW_VAL, &r, &g, &b);
|
||||||
|
|
||||||
|
// apply it to this LED
|
||||||
|
rgb.setPixelColor(i, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment stored hue wheel for the next cycle through the program
|
||||||
|
rainbow_hue += RAINBOW_HUE_INC;
|
||||||
|
if (rainbow_hue > 1536) rainbow_hue -= 1536;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// done with program
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// rgb program 2: circle loops with fading
|
||||||
|
#define CF_TIMEOUT 90 // how long to show this program (max 255, ideally (20*loopcount)+10)
|
||||||
|
#define CF_BRIGHTNESS 64 // how bright to make the LED. don't make too bright or badge will brown out
|
||||||
|
#define CF_FADERATE 12 // how much to fade all LEDs each frame
|
||||||
|
|
||||||
|
uint8_t circlefade_idx = 0;
|
||||||
|
|
||||||
|
uint8_t rgbp_circlefade(uint8_t init)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
uint8_t t;
|
||||||
|
|
||||||
|
// set our timer when initializing. otherwise every call is identical
|
||||||
|
if (init) {
|
||||||
|
prog_timeout = CF_TIMEOUT;
|
||||||
|
circlefade_idx = 4; // top LED
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--prog_timeout) {
|
||||||
|
t = (uint8_t)(CF_TIMEOUT - prog_timeout); // get time elapsed
|
||||||
|
t &= 0x3; // light a new LED every 4th loop
|
||||||
|
|
||||||
|
// fade each LED down every frame
|
||||||
|
for (i = 0; i < (sizeof(rgbled) / sizeof(rgbled[0])); i++) {
|
||||||
|
if (rgbled[i] >= CF_FADERATE) {
|
||||||
|
rgbled[i] -= CF_FADERATE;
|
||||||
|
} else {
|
||||||
|
rgbled[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the next LED in sequence on to full brightness every 4 cycles
|
||||||
|
if (prog_timeout >= 10) { // as long as >10 loops remain,
|
||||||
|
if (!t) { // then on a loop boundary, light the next LED
|
||||||
|
rgb.setPixelColor(circlefade_idx, CF_BRIGHTNESS, CF_BRIGHTNESS, CF_BRIGHTNESS);
|
||||||
|
|
||||||
|
if (++circlefade_idx >= RGB_COUNT) { // then work on the next LED in sequence
|
||||||
|
circlefade_idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
hackspacecon wand firmware
|
||||||
|
|
||||||
|
rgb led control and programs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RGBLED_H__
|
||||||
|
#define __RGBLED_H__
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <tinyNeoPixel_Static.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define RGB_COUNT 5
|
||||||
|
#define PROG_COUNT 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define enable_rgb_timer() {TCB0.CNT = 0; TCB0.CTRLA |= 1;};
|
||||||
|
#define disable_rgb_timer() {TCB0.CTRLA &= ~1;};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern tinyNeoPixel rgb;
|
||||||
|
|
||||||
|
extern uint8_t rgbled[3 * RGB_COUNT];
|
||||||
|
extern uint8_t (*rgb_program[PROG_COUNT])(uint8_t);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void conf_rgb_timer();
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
hackspacecon wand firmware
|
||||||
|
|
||||||
|
initially written by true
|
||||||
|
rgb programs and further hacking by 000000widow
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
this version was hacked to work with AS7.
|
||||||
|
|
||||||
|
the resulting binary at time of porting takes an additional
|
||||||
|
531 bytes of flash and 8 bytes of SRAM. I do not know why.
|
||||||
|
|
||||||
|
|
||||||
|
operation workflow:
|
||||||
|
|
||||||
|
- at initial reset (battery inserted), set up GPIO and peripherals. this
|
||||||
|
includes setting up button wake interrupt. sleep MCU into standby mode.
|
||||||
|
|
||||||
|
- when button state changes, MCU will wake up and process the interrupt.
|
||||||
|
if button is pushed (btn gpio is low), prepare to run next RGB program.
|
||||||
|
|
||||||
|
- rgb program setup involves turning on power to the LEDs and enabling
|
||||||
|
TCB0 periodic timer to interrupt every ~61Hz. this timer interrupt
|
||||||
|
does not handle the rgb program, but will wake the CPU which resumes
|
||||||
|
processing in the loop() function.
|
||||||
|
|
||||||
|
- call the rgb program with the `init` parameter set to 1. idle the CPU.
|
||||||
|
|
||||||
|
- rgb prog timer will interrupt every ~61Hz. this will wake the CPU and
|
||||||
|
send the previously rendered LED data, then call the rgb prog function.
|
||||||
|
afterward, the CPU idles. process repeats until the rgb prog fn returns 0.
|
||||||
|
|
||||||
|
- every time the rgb program is run, the rgb data to send is updated.
|
||||||
|
the data is not actually sent until next wakeup in less than 16.4ms.
|
||||||
|
this allows the functions to have variable processing time, but the
|
||||||
|
output to have a consistent ~61Hz (~16.4ms) timing.
|
||||||
|
|
||||||
|
- once rgb program has finalized, clean up state, disable LED power, and sleep
|
||||||
|
the CPU in standby mode. MCU will wake again once the button is activated.
|
||||||
|
|
||||||
|
|
||||||
|
todo:
|
||||||
|
- test the code
|
||||||
|
- set the RGB output data in the sample rainbow puke program
|
||||||
|
- add more programs
|
||||||
|
- support incrementing or randomly selecting the next program on each button push
|
||||||
|
- run TCB0 in standby mode to save a little more power
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
|
#include "rgbled.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define PIN_LED_PWRENA PIN_PA6
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RGB_IDLE,
|
||||||
|
RGB_INIT,
|
||||||
|
RGB_RUNNING
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROG_RUN = 0,
|
||||||
|
PROG_INIT = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t run_rgbprog = RGB_IDLE;
|
||||||
|
uint8_t rgbprog_idx = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void idle_cpu()
|
||||||
|
{
|
||||||
|
SLPCTRL.CTRLA = SLPCTRL_SMODE_IDLE_gc | SLPCTRL_SEN_bm;
|
||||||
|
__asm("sleep");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleep_cpu()
|
||||||
|
{
|
||||||
|
SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc | SLPCTRL_SEN_bm;
|
||||||
|
__asm("sleep");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// mcu init
|
||||||
|
void setup() {
|
||||||
|
// configure PA2 as falling edge interrupt for button
|
||||||
|
// note: only PA2 and PA6 support async wakeup.
|
||||||
|
// since we're using PA2, we're good to wakeup from a
|
||||||
|
// falling edge (button pushed) event only.
|
||||||
|
PORTA.DIRCLR = PIN2_bm;
|
||||||
|
PORTA.PIN2CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc;
|
||||||
|
|
||||||
|
// configure other hardware pins as appropriate
|
||||||
|
pinMode(PIN_PA3, INPUT_PULLUP); // unused, spare pad on board
|
||||||
|
pinMode(PIN_PA7, INPUT_PULLUP); // unused, voltage passthru
|
||||||
|
|
||||||
|
digitalWrite(PIN_PA6, LOW);
|
||||||
|
pinMode(PIN_PA6, OUTPUT); // LED boost regulator enable
|
||||||
|
|
||||||
|
digitalWrite(PIN_PA1, LOW);
|
||||||
|
pinMode(PIN_PA1, OUTPUT); // LED data
|
||||||
|
|
||||||
|
// set up the RGB ~61Hz periodic timer
|
||||||
|
conf_rgb_timer();
|
||||||
|
|
||||||
|
// enable global interrupts (though they should already be enabled?)
|
||||||
|
sei();
|
||||||
|
}
|
||||||
|
|
||||||
|
// mcu program loop
|
||||||
|
void loop() {
|
||||||
|
switch (run_rgbprog) {
|
||||||
|
case RGB_INIT: { // just started running a program
|
||||||
|
digitalWrite(PIN_LED_PWRENA, HIGH); // enable LED power supply,
|
||||||
|
delay(20); // wait a moment for LEDs to stabilize,
|
||||||
|
|
||||||
|
rgbprog_idx++; // select the next program in sequence,
|
||||||
|
if (rgbprog_idx >= PROG_COUNT) {
|
||||||
|
rgbprog_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rgb_program[rgbprog_idx](PROG_INIT);// initialize the program,
|
||||||
|
run_rgbprog++; // and set to running mode.
|
||||||
|
|
||||||
|
enable_rgb_timer(); // then start the RGB program timebase.
|
||||||
|
|
||||||
|
idle_cpu(); // we can idle CPU after running the program
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RGB_RUNNING: { // continuing to run a program
|
||||||
|
rgb.show(); // send updates to the led
|
||||||
|
// then process the next program frame
|
||||||
|
if (!rgb_program[rgbprog_idx](PROG_RUN)) {
|
||||||
|
run_rgbprog = RGB_IDLE; // until the program says it's done
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
idle_cpu(); // we can idle CPU after running the program
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: { // no longer running a program
|
||||||
|
disable_rgb_timer(); // disable RGB program timer,
|
||||||
|
digitalWrite(PIN_LED_PWRENA, LOW); // disable LED power supply,
|
||||||
|
run_rgbprog = RGB_IDLE; // and clear run_rgbprog.
|
||||||
|
|
||||||
|
sleep_cpu(); // finally, go to sleep in standby mode
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
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}") = "Arduino", "Arduino\Arduino.cppproj", "{47694D30-4990-4F03-A1F7-E94EB79783F0}"
|
||||||
|
EndProject
|
||||||
|
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "HSC_Wand", "HSC_Wand\HSC_Wand.cppproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|AVR = Debug|AVR
|
||||||
|
Release|AVR = Release|AVR
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{47694D30-4990-4F03-A1F7-E94EB79783F0}.Debug|AVR.ActiveCfg = Debug|AVR
|
||||||
|
{47694D30-4990-4F03-A1F7-E94EB79783F0}.Debug|AVR.Build.0 = Debug|AVR
|
||||||
|
{47694D30-4990-4F03-A1F7-E94EB79783F0}.Release|AVR.ActiveCfg = Release|AVR
|
||||||
|
{47694D30-4990-4F03-A1F7-E94EB79783F0}.Release|AVR.Build.0 = Release|AVR
|
||||||
|
{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
|
Loading…
Reference in New Issue