diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..761da0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,351 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + + +# Custom ignore +__vm/ +*.vcxproj +*.vcxproj.filters +*.vsarduino.h +*.vmps.xml +*.Debug.vmps.xml + + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ii]ntermediates/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb \ No newline at end of file diff --git a/README.adoc b/README.adoc deleted file mode 100644 index edabb1a..0000000 --- a/README.adoc +++ /dev/null @@ -1,35 +0,0 @@ -= Servo Hardware PWM Library for Arduino Mega = - -This library allows Arduino/Genuino Mega boards to control up to **6 servos** with the integrated **16-bit hardware PWM timer/counter**. -This library uses the 16-bit hardware timer/counter (timer3, timer4 and timer5) to control the servos. - -Unlike the original Servo.h library, this library does not use timer1. -The advantage here is that when using the Wire.h library no fluctuations in the pulse width occur. -In addition, I/O registers are addressed directly and not via the digitalWrite()-function (as in Servo.h). - -Possible output pins are **pin 2, 3, 7, 8, 44,** and **45**. -**Only Arduino/Genuino Mega boards are supported!** - -== License == - -MIT License - -Copyright (c) 2019 Daniel Duller - -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..86a2717 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# Servo Hardware PWM Library for Arduino Mega + +This library allows Arduino/Genuino Mega boards to control up to **6 servos** with the integrated **16-bit hardware PWM timer/counter**. +16-bit hardware timer/counter (timer3, timer4 and timer5) are used to control the servos. + +Unlike the original Servo.h library, this library does not use timer1. +The advantage here is that when using the Wire.h library no fluctuations in the pulse width occur. +In addition, I/O registers are addressed directly and not via the digitalWrite()-function (as in Servo.h). + +Possible output pins are **pin 2, 3, 7, 8, 44,** and **45**. +**Only Arduino/Genuino Mega boards are supported!** + +### Installation +This library can be installed through the Arduino IDE library manager like so: +![](installation.gif) + +### Note +Starting from version 1.2.0 this Servo-Library is compatible with all the [original Arduino Servo Library](https://github.com/arduino-libraries/Servo) - commands available. In addition to these "standard"-functions, following commands are added: +* ``` attach(int pin, int min, int max, int defaultPos)``` - Besides the ability to set the servo pin and the upper and lower pulse width limit, the starting pulse width of the servo can be set with the defaultPos. This allows the servo to start from a user-defined angle instead of the middle position. +* ```detachAll()``` - This feature allows detaching all servos at once. + +### Built With +* [Visual Studio 2019](https://visualstudio.microsoft.com/) - IDE used for programming +* [Arduino IDE](https://www.arduino.cc/en/Main/Software) - IDE used for programming and testing + +### Author +**Daniel Duller** - [dadul96](https://github.com/dadul96) + +### License +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details + +### Acknowledgments +* Inspired by - [original Arduino Servo Library](https://github.com/arduino-libraries/Servo) +* Thanks for helping me to improve my library - [per1234](https://github.com/per1234) +* Screen-GIF recorded with - [ShareX](https://getsharex.com/) diff --git a/examples/Servo_Sweep/Servo_Sweep.ino b/examples/Servo_Sweep/Servo_Sweep.ino index c0ba682..402e70b 100644 --- a/examples/Servo_Sweep/Servo_Sweep.ino +++ b/examples/Servo_Sweep/Servo_Sweep.ino @@ -1,5 +1,6 @@ /* Servo Sweep - by Daniel Duller 12. January 2019 + Created by Daniel Duller, 12. January, 2019. + Changed by Daniel Duller, 11. October, 2019. This example code is in the public domain. */ @@ -13,6 +14,7 @@ Servo myServo5; Servo myServo6; unsigned int valueMicros = 0; //variable that contains the microseconds +int valueDegrees = 0; //variable that contains the degrees void setup() { myServo1.attach(2); //attaches the servo to pin 2 @@ -24,22 +26,25 @@ void setup() { } void loop() { - for (valueMicros = 500; valueMicros < 2500; valueMicros++){ //goes from 0° to 180° (500us to 2500us) + //option 1 - using microseconds and the writeMicroseconds-function: + for (valueMicros = 500; valueMicros < 2500; valueMicros++){ //goes from 500us to 2500us (0° to 180°) myServo1.writeMicroseconds(valueMicros); //writes the value of valueMicros to the servo myServo2.writeMicroseconds(valueMicros); myServo3.writeMicroseconds(valueMicros); myServo4.writeMicroseconds(valueMicros); myServo5.writeMicroseconds(valueMicros); myServo6.writeMicroseconds(valueMicros); - delay(10); + delay(1); } - for (valueMicros = 2500; valueMicros > 500; valueMicros--){ //goes from 180° to 0° (2500us to 500us) - myServo1.writeMicroseconds(valueMicros); - myServo2.writeMicroseconds(valueMicros); - myServo3.writeMicroseconds(valueMicros); - myServo4.writeMicroseconds(valueMicros); - myServo5.writeMicroseconds(valueMicros); - myServo6.writeMicroseconds(valueMicros); + + //option 2 - using degrees and the write-function: + for (valueDegrees = 180; valueDegrees > 0; valueDegrees--){ //goes from 180° to 0° (2500us to 500us) + myServo1.write(valueDegrees); //writes the value of valueDegrees to the servo + myServo2.write(valueDegrees); + myServo3.write(valueDegrees); + myServo4.write(valueDegrees); + myServo5.write(valueDegrees); + myServo6.write(valueDegrees); delay(10); } } diff --git a/extras/functions_explained.adoc b/extras/functions_explained.adoc index 543c3e1..2255e05 100644 --- a/extras/functions_explained.adoc +++ b/extras/functions_explained.adoc @@ -4,6 +4,20 @@ The functions of the library are: **attach(pin)** -- Attaches a servo motor to an i/o pin. (only **pin 2, 3, 7, 8, 44,** and **45**) -**writeMicroseconds(us)** -- Sets the servo pulse width in microseconds. (usable range between **500**us and **2500**us) +**attach(pin, min, max)** -- Attaches a servo motor to an i/o pin with a custom lower and upper pulse width limit. + +**attach(pin, min, max, defaultPos)** -- Attaches a servo motor to an i/o pin with a custom lower and upper pulse width limit. In addition, the starting pulse width of the servo can be set with the defaultPos. This allows the servo to start from a user-defined angle instead of the middle position. **detach()** -- Clears the used registers and stops the output of the attached pin. (**no pin number!**) + +**detachAll()** -- Clears the used registers and stops the output of ALL attached pins. (**no pin number!**) + +**write(degrees)** -- Sets the servo angle in degrees. (usable range between 0° and 180°) + +**writeMicroseconds(us)** -- Sets the servo pulse width in microseconds. (standard usable range between **500**us and **2500**us) + +**read()** -- Returns the current angle (in degrees) that gets transmitted to the servo. (return type: **int**) + +**readMicroseconds()** -- Returns the current angle (in microseconds) that gets transmitted to the servo. (return type: **int**) + +**attached()** -- Returns **true** if the servo is attached. (return type: **bool**) diff --git a/installation.gif b/installation.gif new file mode 100644 index 0000000..4537ce2 Binary files /dev/null and b/installation.gif differ diff --git a/keywords.txt b/keywords.txt index f364eb0..323ad38 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,7 +13,12 @@ Servo KEYWORD1 Servo ####################################### attach KEYWORD2 detach KEYWORD2 +detachAll KEYWORD2 +write KEYWORD2 writeMicroseconds KEYWORD2 +read KEYWORD2 +readMicroseconds KEYWORD2 +attached KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/library.properties b/library.properties index 25e9631..9668c10 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Servo Hardware PWM -version=1.0.2 +version=1.2.0 author=Daniel Duller maintainer=Daniel Duller sentence=Allows Arduino/Genuino Mega boards to control up to 6 servos with the integrated 16-bit hardware PWM timer/counter. diff --git a/src/Servo_Hardware_PWM.cpp b/src/Servo_Hardware_PWM.cpp index c491dec..153cdad 100644 --- a/src/Servo_Hardware_PWM.cpp +++ b/src/Servo_Hardware_PWM.cpp @@ -7,21 +7,45 @@ For information about the library, license and author, see Servo_Hardware_PWM.h #include #include -int servoCount = 0; -bool pinActive[MAX_SERVOS] = {false}; +#define BOOL_FALSE 0 +#define BOOL_TRUE 255 -Servo::Servo() -{ - if (servoCount < MAX_SERVOS) { +static uint8_t servoCount = 0; +static uint8_t pinActive[MAX_SERVOS] = {BOOL_FALSE}; + +Servo::Servo() { + this->servoIndex = 0; + this->servoPin = 0; + + if (servoCount < MAX_SERVOS) + { this->servoIndex = servoCount++; //assign a servoIndex to this instance } + else + { + this->servoIndex = INVALID_SERVO_NUMBER; //too many servos + } +} + +uint8_t Servo::attach(int pin) { + return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH, DEFAULT_PULSE_WIDTH); +} + +uint8_t Servo::attach(int pin, int min, int max) { + return this->attach(pin, min, max, DEFAULT_PULSE_WIDTH); } -void Servo::attach(int pin) -{ - if (this->servoIndex < MAX_SERVOS) { - if (pin == 2) { - if(pinActive[1] == false){ +uint8_t Servo::attach(int pin, int min, int max, int defaultPos) { + this->min = min; + this->max = max; + this->defaultPos = defaultPos * 2; + + if (this->servoIndex < MAX_SERVOS) + { + if (pin == 2) + { + if (pinActive[1] == BOOL_FALSE) + { //resetting the control register A and B: TCCR3A = 0x0; TCCR3B = 0x0; @@ -34,22 +58,24 @@ void Servo::attach(int pin) TCCR3A |= (1 << WGM31) | (1 << WGM30); TCCR3B |= (1 << WGM32) | (1 << WGM33); - //setting the the TOP value: + //setting the TOP value: OCR3A = MAX_TIMER_COUNT; //results in 50Hz at 2MHz Clock //setting the output to non inverted: TCCR3A |= (1 << COM3B1); - OCR3B = DEFAULT_PULSE_WIDTH; //setting the pulse width + OCR3B = this->defaultPos; //setting the pulse width //OC3B, Port E, Bit 4; setting pin 2 as output: DDRE |= (1 << PE4); //bit 4 (pin 2) as output - - pinActive[0] = true; + + pinActive[0] = BOOL_TRUE; this->servoPin = 2; } - else if (pin == 3) { - if(pinActive[0] == false){ + else if (pin == 3) + { + if (pinActive[0] == BOOL_FALSE) + { //resetting the control register A and B: TCCR3A = 0x0; TCCR3B = 0x0; @@ -62,22 +88,24 @@ void Servo::attach(int pin) TCCR3A |= (1 << WGM31) | (1 << WGM30); TCCR3B |= (1 << WGM32) | (1 << WGM33); - //setting the the TOP value: + //setting the TOP value: OCR3A = MAX_TIMER_COUNT; //results in 50Hz at 2MHz Clock //setting the output to non inverted: TCCR3A |= (1 << COM3C1); - OCR3C = DEFAULT_PULSE_WIDTH; //setting the pulse width + OCR3C = this->defaultPos; //setting the pulse width //OC3C, Port E, Bit 5; setting pin 3 as output: DDRE |= (1 << PE5); //bit 5 (pin 3) as output - - pinActive[1] = true; + + pinActive[1] = BOOL_TRUE; this->servoPin = 3; } - else if (pin == 7) { - if(pinActive[3] == false){ + else if (pin == 7) + { + if (pinActive[3] == BOOL_FALSE) + { //resetting the control register A and B: TCCR4A = 0x0; TCCR4B = 0x0; @@ -90,22 +118,24 @@ void Servo::attach(int pin) TCCR4A |= (1 << WGM41) | (1 << WGM40); TCCR4B |= (1 << WGM42) | (1 << WGM43); - //setting the the TOP value: + //setting the TOP value: OCR4A = MAX_TIMER_COUNT; //results in 50Hz at 2MHz Clock //setting the output to non inverted: TCCR4A |= (1 << COM4B1); - OCR4B = DEFAULT_PULSE_WIDTH; //setting the pulse width + OCR4B = this->defaultPos; //setting the pulse width //OC4B, Port H, Bit 4; setting pin 7 as output: DDRH |= (1 << PH4); //bit 4 (pin 7) as output - - pinActive[2] = true; + + pinActive[2] = BOOL_TRUE; this->servoPin = 7; } - else if (pin == 8) { - if(pinActive[2] == false){ + else if (pin == 8) + { + if (pinActive[2] == BOOL_FALSE) + { //resetting the control register A and B: TCCR4A = 0x0; TCCR4B = 0x0; @@ -118,27 +148,29 @@ void Servo::attach(int pin) TCCR4A |= (1 << WGM41) | (1 << WGM40); TCCR4B |= (1 << WGM42) | (1 << WGM43); - //setting the the TOP value: + //setting the TOP value: OCR4A = MAX_TIMER_COUNT; //results in 50Hz at 2MHz Clock //setting the output to non inverted: TCCR4A |= (1 << COM4C1); - OCR4C = DEFAULT_PULSE_WIDTH; //setting the pulse width + OCR4C = this->defaultPos; //setting the pulse width //OC4C, Port H, Bit 5; setting pin 8 as output: DDRH |= (1 << PH5); //bit 5 (pin 8) as output - - pinActive[3] = true; + + pinActive[3] = BOOL_TRUE; this->servoPin = 8; } - else if (pin == 44) { - if(pinActive[5] == false){ + else if (pin == 44) + { + if (pinActive[5] == BOOL_FALSE) + { //resetting the control register A and B: TCCR5A = 0x0; TCCR5B = 0x0; } - + //setting the prescaler to 8 (2MHz): TCCR5B |= (1 << CS51); @@ -146,22 +178,24 @@ void Servo::attach(int pin) TCCR5A |= (1 << WGM51) | (1 << WGM50); TCCR5B |= (1 << WGM52) | (1 << WGM53); - //setting the the TOP value: + //setting the TOP value: OCR5A = MAX_TIMER_COUNT; //results in 50Hz at 2MHz Clock //setting the output to non inverted: TCCR5A |= (1 << COM5C1); - OCR5C = DEFAULT_PULSE_WIDTH; //setting the pulse width + OCR5C = this->defaultPos; //setting the pulse width //OC5C, Port L, Bit 5; setting pin 44 as output: DDRL |= (1 << PL5); //bit 5 (pin 44) as output - - pinActive[4] = true; + + pinActive[4] = BOOL_TRUE; this->servoPin = 44; } - else if (pin == 45) { - if(pinActive[4] == false){ + else if (pin == 45) + { + if (pinActive[4] == BOOL_FALSE) + { //resetting the control register A and B: TCCR5A = 0x0; TCCR5B = 0x0; @@ -174,27 +208,30 @@ void Servo::attach(int pin) TCCR5A |= (1 << WGM51) | (1 << WGM50); TCCR5B |= (1 << WGM52) | (1 << WGM53); - //setting the the TOP value: + //setting the TOP value: OCR5A = MAX_TIMER_COUNT; //results in 50Hz at 2MHz Clock //setting the output to non inverted: TCCR5A |= (1 << COM5B1); - OCR5B = DEFAULT_PULSE_WIDTH; //setting the pulse width + OCR5B = this->defaultPos; //setting the pulse width //OC5B, Port L, Bit 4; setting pin 45 as output: DDRL |= (1 << PL4); //bit 4 (pin 45) as output - - pinActive[5] = true; + + pinActive[5] = BOOL_TRUE; this->servoPin = 45; } } + + return this->servoIndex; } -void Servo::detach() -{ - if (servoPin == 2) { - if(pinActive[1] == false){ +void Servo::detach() { + if (servoPin == 2 && pinActive[0] == BOOL_TRUE) + { + if (pinActive[1] == BOOL_FALSE) + { //resetting the control register A and B: TCCR3A = 0x0; TCCR3B = 0x0; @@ -203,11 +240,13 @@ void Servo::detach() } OCR3B = 0x0; //resetting the pulse width DDRE ^= (1 << PE4); //bit 4 (pin 2) stop output - pinActive[0] = false; - this->servoPin = 0; + pinActive[0] = BOOL_FALSE; + this->servoPin = 0; } - else if (servoPin == 3) { - if(pinActive[0] == false){ + else if (servoPin == 3 && pinActive[1] == BOOL_TRUE) + { + if (pinActive[0] == BOOL_FALSE) + { //resetting the control register A and B: TCCR3A = 0x0; TCCR3B = 0x0; @@ -216,11 +255,13 @@ void Servo::detach() } OCR3C = 0x0; //resetting the pulse width DDRE ^= (1 << PE5); //bit 5 (pin 3) stop output - pinActive[1] = false; + pinActive[1] = BOOL_FALSE; this->servoPin = 0; } - else if (servoPin == 7) { - if(pinActive[3] == false){ + else if (servoPin == 7 && pinActive[2] == BOOL_TRUE) + { + if (pinActive[3] == BOOL_FALSE) + { //resetting the control register A and B: TCCR4A = 0x0; TCCR4B = 0x0; @@ -229,11 +270,13 @@ void Servo::detach() } OCR4B = 0x0; //resetting the pulse width DDRH ^= (1 << PH4); //bit 4 (pin 7) stop output - pinActive[2] = false; + pinActive[2] = BOOL_FALSE; this->servoPin = 0; - } - else if (servoPin == 8) { - if(pinActive[2] == false){ + } + else if (servoPin == 8 && pinActive[3] == BOOL_TRUE) + { + if (pinActive[2] == BOOL_FALSE) + { //resetting the control register A and B: TCCR4A = 0x0; TCCR4B = 0x0; @@ -242,11 +285,13 @@ void Servo::detach() } OCR4C = 0x0; //resetting the pulse width DDRH ^= (1 << PH5); //bit 5 (pin 8) stop output - pinActive[3] = false; + pinActive[3] = BOOL_FALSE; this->servoPin = 0; } - else if (servoPin == 44) { - if(pinActive[5] == false){ + else if (servoPin == 44 && pinActive[4] == BOOL_TRUE) + { + if (pinActive[5] == BOOL_FALSE) + { //resetting the control register A and B: TCCR5A = 0x0; TCCR5B = 0x0; @@ -255,11 +300,13 @@ void Servo::detach() } OCR5C = 0x0; //resetting the pulse width DDRL ^= (1 << PL5); //bit 5 (pin 44) stop output - pinActive[4] = false; + pinActive[4] = BOOL_FALSE; this->servoPin = 0; } - else if (servoPin == 45) { - if(pinActive[4] == false){ + else if (servoPin == 45 && pinActive[5] == BOOL_TRUE) + { + if (pinActive[4] == BOOL_FALSE) + { //resetting the control register A and B: TCCR5A = 0x0; TCCR5B = 0x0; @@ -268,45 +315,179 @@ void Servo::detach() } OCR5B = 0x0; //resetting the pulse width DDRL ^= (1 << PL4); //bit 4 (pin 45) stop output - pinActive[5] = false; + pinActive[5] = BOOL_FALSE; this->servoPin = 0; } } +void Servo::detachAll() { + if (pinActive[0] == BOOL_TRUE) + { + //resetting the control register A and B: + TCCR3A = 0x0; + TCCR3B = 0x0; + //resetting the TOP value: + OCR3A = 0x0; + + OCR3B = 0x0; //resetting the pulse width + DDRE ^= (1 << PE4); //bit 4 (pin 2) stop output + pinActive[0] = BOOL_FALSE; + } + if (pinActive[1] == BOOL_TRUE) + { + //resetting the control register A and B: + TCCR3A = 0x0; + TCCR3B = 0x0; + //resetting the TOP value: + OCR3A = 0x0; + + OCR3C = 0x0; //resetting the pulse width + DDRE ^= (1 << PE5); //bit 5 (pin 3) stop output + pinActive[1] = BOOL_FALSE; + } + if (pinActive[2] == BOOL_TRUE) + { + //resetting the control register A and B: + TCCR4A = 0x0; + TCCR4B = 0x0; + //resetting the TOP value: + OCR4A = 0x0; + + OCR4B = 0x0; //resetting the pulse width + DDRH ^= (1 << PH4); //bit 4 (pin 7) stop output + pinActive[2] = BOOL_FALSE; + } + if (pinActive[3] == BOOL_TRUE) + { + //resetting the control register A and B: + TCCR4A = 0x0; + TCCR4B = 0x0; + //resetting the TOP value: + OCR4A = 0x0; + + OCR4C = 0x0; //resetting the pulse width + DDRH ^= (1 << PH5); //bit 5 (pin 8) stop output + pinActive[3] = BOOL_FALSE; + } + if (pinActive[4] == BOOL_TRUE) + { + //resetting the control register A and B: + TCCR5A = 0x0; + TCCR5B = 0x0; + //resetting the TOP value: + OCR5A = 0x0; + + OCR5C = 0x0; //resetting the pulse width + DDRL ^= (1 << PL5); //bit 5 (pin 44) stop output + pinActive[4] = BOOL_FALSE; + } + if (pinActive[5] == BOOL_TRUE) + { + //resetting the control register A and B: + TCCR5A = 0x0; + TCCR5B = 0x0; + //resetting the TOP value: + OCR5A = 0x0; + + OCR5B = 0x0; //resetting the pulse width + DDRL ^= (1 << PL4); //bit 4 (pin 45) stop output + pinActive[5] = BOOL_FALSE; + } +} + +void Servo::write(int value) { + float tempValue; + + if (value <= 0) + { + tempValue = 0.0; + } + else if (value >= 180) + { + tempValue = 180.0; + } + else + { + tempValue = (float)value; + } + + tempValue = (((this->max - this->min) * tempValue) / 180.0) + this->min; + + value = (int)tempValue; + this->writeMicroseconds(value); +} -void Servo::writeMicroseconds(int value) -{ - if ((this->servoIndex < MAX_SERVOS)) { - if (value < MIN_PULSE_WIDTH) { - value = MIN_PULSE_WIDTH; +void Servo::writeMicroseconds(int value) { + if ((this->servoIndex < MAX_SERVOS)) + { + if (value < this->min) + { + value = this->min; } - else if (value > MAX_PULSE_WIDTH) { - value = MAX_PULSE_WIDTH; + else if (value > this->max) + { + value = this->max; } - if (this->servoPin == 2) { + this->pulseWidth = value; + + if (this->servoPin == 2 && pinActive[0] == BOOL_TRUE) + { OCR3B = 0x0; - OCR3B = value * 2; + OCR3B = this->pulseWidth * 2; } - else if (this->servoPin == 3) { + else if (this->servoPin == 3 && pinActive[1] == BOOL_TRUE) + { OCR3C = 0x0; - OCR3C = value * 2; + OCR3C = this->pulseWidth * 2; } - else if (this->servoPin == 7) { + else if (this->servoPin == 7 && pinActive[2] == BOOL_TRUE) + { OCR4B = 0x0; - OCR4B = value * 2; + OCR4B = this->pulseWidth * 2; } - else if (this->servoPin == 8) { + else if (this->servoPin == 8 && pinActive[3] == BOOL_TRUE) + { OCR4C = 0x0; - OCR4C = value * 2; + OCR4C = this->pulseWidth * 2; } - else if (this->servoPin == 44) { + else if (this->servoPin == 44 && pinActive[4] == BOOL_TRUE) + { OCR5C = 0x0; - OCR5C = value * 2; + OCR5C = this->pulseWidth * 2; } - else if (this->servoPin == 45) { + else if (this->servoPin == 45 && pinActive[5] == BOOL_TRUE) + { OCR5B = 0x0; - OCR5B = value * 2; + OCR5B = this->pulseWidth * 2; } } } + +int Servo::read() { + float angle; + + if ((this->readMicroseconds() - this->min) <= 0) + { + angle = 0.0; + } + else + { + angle = (180.0 / (this->max - this->min)) * (this->readMicroseconds() - this->min); + } + + return (int)angle; +} + +int Servo::readMicroseconds() { + if (this->servoIndex == INVALID_SERVO_NUMBER) + { + this->pulseWidth = 0; + } + + return this->pulseWidth; +} + +bool Servo::attached() { + return pinActive[this->servoIndex] == BOOL_TRUE; +} #endif diff --git a/src/Servo_Hardware_PWM.h b/src/Servo_Hardware_PWM.h index 75147e5..370b665 100644 --- a/src/Servo_Hardware_PWM.h +++ b/src/Servo_Hardware_PWM.h @@ -1,7 +1,7 @@ /* Servo_Hardware_PWM.h - This Library allows Arduino/Genuino Mega boards to control up to 6 servos with the integrated 16-bit hardware PWM timer/counter. Created by Daniel Duller, 11. January, 2019. - Changed by Daniel Duller, 02. July, 2019. + Changed by Daniel Duller, 11. October, 2019. ############################################################################### MIT License @@ -27,51 +27,81 @@ SOFTWARE. ############################################################################### - + ######################################################### Only works on pin 2, 3, 7, 8, 44, and 45 on Arduino Mega! ######################################################### Tested on Arduino Mega 2560 R3. ######################################################### - + The methods are: - Servo --- Class for manipulating servo motors connected to Arduino pins. (max. 6 elements) + Servo --- Class for manipulating servo motors connected to Arduino pins. (max. 6 elements) + + attach(pin) --- Attaches a servo motor to an i/o pin. (only pin 2, 3, 7, 8, 44, and 45) + + attach(pin, min, max) --- Attaches a servo motor to an i/o pin with a custom lower and upper pulse width limit. + + attach(pin, min, max, defaultPos) --- Attaches a servo motor to an i/o pin with a custom lower and upper pulse width limit. + In addition, the starting pulse width of the servo can be set with the defaultPos. + This allows the servo to start from a user-defined angle instead of the middle position. + + detach() --- Clears the used registers and stops the output of the attached pin. (no pin number!) - attach(pin) --- Attaches a servo motor to an i/o pin. (only pin 2, 3, 7, 8, 44, and 45) + detachAll() --- Clears the used registers and stops the output of ALL attached pins. (no pin number!) - writeMicroseconds(us) --- Sets the servo pulse width in microseconds. (usable range between 500us and 2500us) + write(degrees) --- Sets the servo angle in degrees. (usable range between 0° and 180°) - detach() --- Clears the used registers and stops the output of the attached pin. (no pin number!) + writeMicroseconds(us) --- Sets the servo pulse width in microseconds. (usable range between 500us and 2500us) + + read() --- Returns the current angle (in degrees) that gets transmitted to the servo. (return type: int) + + readMicroseconds() --- Returns the current angle (in microseconds) that gets transmitted to the servo. (return type: int) + + attached() --- Returns true if the servo is attached. (return type: bool) */ #ifndef Servo_Hardware_PWM_h #define Servo_Hardware_PWM_h +#include + #if !defined(ARDUINO_ARCH_AVR) #error "This library only supports boards with an AVR processor." #endif -#define Servo_VERSION 1.0.2 //software version of this library +#define Servo_VERSION 1.0.1 //software version of this library #define MIN_PULSE_WIDTH 500 //the shortest pulse sent to a servo #define MAX_PULSE_WIDTH 2500 //the longest pulse sent to a servo -#define DEFAULT_PULSE_WIDTH 0 //default pulse width when servo is attached +#define DEFAULT_PULSE_WIDTH 1500 //default pulse width when servo is attached #define MAX_TIMER_COUNT 40000 //the timer TOP value (for creating 50Hz) #define MAX_SERVOS 6 //6 Servos can be attached +#define INVALID_SERVO_NUMBER 255 //flag indicating an invalid servo index -class Servo -{ +class Servo { public: Servo(); - void attach(int pin); //attach the given pin - void detach(); //detach the used pin - void writeMicroseconds(int value); //write pulse width in microseconds + uint8_t attach(int pin); //attach the given pin; returns servoIndex number or 255 if too many servos + uint8_t attach(int pin, int min, int max); //attach the given pin and set the upper and lower pulse width limits; returns servoIndex number or 255 if too many servos + uint8_t attach(int pin, int min, int max, int defaultPos); //attach the given pin, set the upper and lower pulse width limits and set the pulse width when servo is attached; returns servoIndex number or 255 if too many servos + void detach(); //detach the used pin + void detachAll(); //automatically detaches all used pins + void write(int value); //write angle in degrees + void writeMicroseconds(int value); //write pulse width in microseconds + int read(); //returns the current write angle in degrees + int readMicroseconds(); //returns the current write angle in microseconds + bool attached(); //returns true if this servo is attached, otherwise false + private: - int servoIndex = 0; //number of attached Servos - int servoPin = 0; //pin number of the attached Servo + uint8_t servoIndex; //number of attached Servos + uint8_t servoPin; //pin number of the attached Servo + uint16_t min; //lower pulse width limit + uint16_t max; //upper pulse width limit + uint16_t defaultPos; //pulse width when servo is attached + uint16_t pulseWidth; //set pulse width }; #endif