初始化提交
This commit is contained in:
267
arduino-cli/libraries/ESP32_Servo/ESP32_Servo.cpp
Normal file
267
arduino-cli/libraries/ESP32_Servo/ESP32_Servo.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
Copyright (c) 2017 John K. Bennett. 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
|
||||
|
||||
* Notes on the implementation:
|
||||
* The ESP32 supports 16 hardware LED PWM channels that are intended
|
||||
* to be used for LED brightness control. The low level ESP32 code
|
||||
* (esp32-hal-ledc.*) allows us to set the PWM frequency and bit-depth,
|
||||
* and then manipulate them by setting bits in the relevant control
|
||||
* registers.
|
||||
*
|
||||
* Different servos require different pulse widths to vary servo angle, but the range is
|
||||
* an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos
|
||||
* sweep 180 degrees, so the lowest number in the published range for a particular servo
|
||||
* represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top
|
||||
* of the range represents 180 degrees. So for example, if the range is 1000us to 2000us,
|
||||
* 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 180
|
||||
* degrees. We vary pulse width (recall that the pulse period is already set to 20ms) as follows:
|
||||
*
|
||||
* The ESP32 PWM timers allow us to set the timer width (max 20 bits). Thus
|
||||
* the timer "tick" length is (pulse_period/2**timer_width), and the equation for pulse_high_width
|
||||
* (the portion of the 20ms cycle that the signal is high) becomes:
|
||||
*
|
||||
* pulse_high_width = count * tick_length
|
||||
* = count * (pulse_period/2**timer_width)
|
||||
*
|
||||
* and count = (pulse_high_width / (pulse_period/2**timer_width))
|
||||
*
|
||||
* So, for example, if I want a 1500us pulse_high_width, I set pulse_period to 20ms (20000us)
|
||||
* (this value is set in the ledcSetup call), and count (used in the ledcWrite call) to
|
||||
* 1500/(20000/65536), or 4924. This is the value we write to the timer in the ledcWrite call.
|
||||
* If we increase the timer_width, the timer_count values need to be adjusted.
|
||||
*
|
||||
* The servo signal pins connect to any available GPIO pins on the ESP32, but not all pins are
|
||||
* GPIO pins.
|
||||
*
|
||||
* The ESP32 is a 32 bit processor that includes FP support; this code reflects that fact.
|
||||
*/
|
||||
|
||||
#include "ESP32_Servo.h"
|
||||
#include "esp32-hal-ledc.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
// initialize the class variable ServoCount
|
||||
int Servo::ServoCount = 0;
|
||||
|
||||
// The ChannelUsed array elements are 0 if never used, 1 if in use, and -1 if used and disposed
|
||||
// (i.e., available for reuse)
|
||||
int Servo::ChannelUsed[MAX_SERVOS+1] = {0}; // we ignore the zeroth element
|
||||
|
||||
Servo::Servo()
|
||||
{
|
||||
this->servoChannel = 0;
|
||||
// see if there is a servo channel available for reuse
|
||||
bool foundChannelForReuse = false;
|
||||
for (int i = 1; i < MAX_SERVOS+1; i++)
|
||||
{
|
||||
if (ChannelUsed[i] == -1)
|
||||
{
|
||||
// reclaim this channel
|
||||
ChannelUsed[i] = 1;
|
||||
this->servoChannel = i;
|
||||
foundChannelForReuse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundChannelForReuse)
|
||||
{
|
||||
// no channels available for reuse; get a new one if we can
|
||||
if (ServoCount < MAX_SERVOS)
|
||||
{
|
||||
this->servoChannel = ++ServoCount; // assign a servo channel number to this instance
|
||||
ChannelUsed[this->servoChannel] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->servoChannel = 0; // too many servos in use
|
||||
}
|
||||
}
|
||||
// if we got a channel either way, finish initializing it
|
||||
if (this->servoChannel > 0)
|
||||
{
|
||||
// initialize this channel with plausible values, except pin # (we set pin # when attached)
|
||||
this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
|
||||
this->timer_width = DEFAULT_TIMER_WIDTH;
|
||||
this->pinNumber = -1; // make it clear that we haven't attached a pin to this channel
|
||||
this->min = DEFAULT_uS_LOW;
|
||||
this->max = DEFAULT_uS_HIGH;
|
||||
this->timer_width_ticks = pow(2,this->timer_width);
|
||||
}
|
||||
}
|
||||
|
||||
int Servo::attach(int pin)
|
||||
{
|
||||
return (this->attach(pin, DEFAULT_uS_LOW, DEFAULT_uS_HIGH));
|
||||
}
|
||||
|
||||
int Servo::attach(int pin, int min, int max)
|
||||
{
|
||||
if ((this->servoChannel <= MAX_SERVOS) && (this->servoChannel > 0))
|
||||
{
|
||||
// Recommend only the following pins 2,4,12-19,21-23,25-27,32-33 (enforcement commented out)
|
||||
//if ((pin == 2) || (pin ==4) || ((pin >= 12) && (pin <= 19)) || ((pin >= 21) && (pin <= 23)) ||
|
||||
// ((pin >= 25) && (pin <= 27)) || (pin == 32) || (pin == 33))
|
||||
//{
|
||||
// OK to proceed; first check for new/reuse
|
||||
if (this->pinNumber < 0) // we are attaching to a new or previously detached pin; we need to initialize/reinitialize
|
||||
{
|
||||
// claim/reclaim this channel
|
||||
ChannelUsed[this->servoChannel] = 1;
|
||||
this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
|
||||
this->timer_width = DEFAULT_TIMER_WIDTH;
|
||||
this->timer_width_ticks = pow(2,this->timer_width);
|
||||
}
|
||||
this->pinNumber = pin;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
// min/max checks
|
||||
if (min < MIN_PULSE_WIDTH) // ensure pulse width is valid
|
||||
min = MIN_PULSE_WIDTH;
|
||||
if (max > MAX_PULSE_WIDTH)
|
||||
max = MAX_PULSE_WIDTH;
|
||||
this->min = min; //store this value in uS
|
||||
this->max = max; //store this value in uS
|
||||
// Set up this channel
|
||||
// if you want anything other than default timer width, you must call setTimerWidth() before attach
|
||||
ledcSetup(this->servoChannel, REFRESH_CPS, this->timer_width); // channel #, 50 Hz, timer width
|
||||
ledcAttachPin(this->pinNumber, this->servoChannel); // GPIO pin assigned to channel
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void Servo::detach()
|
||||
{
|
||||
if (this->attached())
|
||||
{
|
||||
ledcDetachPin(this->pinNumber);
|
||||
//keep track of detached servos channels so we can reuse them if needed
|
||||
ChannelUsed[this->servoChannel] = -1;
|
||||
this->pinNumber = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Servo::write(int value)
|
||||
{
|
||||
// treat values less than MIN_PULSE_WIDTH (500) as angles in degrees (valid values in microseconds are handled as microseconds)
|
||||
if (value < MIN_PULSE_WIDTH)
|
||||
{
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
else if (value > 180)
|
||||
value = 180;
|
||||
|
||||
value = map(value, 0, 180, this->min, this->max);
|
||||
}
|
||||
this->writeMicroseconds(value);
|
||||
}
|
||||
|
||||
void Servo::writeMicroseconds(int value)
|
||||
{
|
||||
// calculate and store the values for the given channel
|
||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached())) // ensure channel is valid
|
||||
{
|
||||
if (value < this->min) // ensure pulse width is valid
|
||||
value = this->min;
|
||||
else if (value > this->max)
|
||||
value = this->max;
|
||||
|
||||
value = usToTicks(value); // convert to ticks
|
||||
this->ticks = value;
|
||||
// do the actual write
|
||||
ledcWrite(this->servoChannel, this->ticks);
|
||||
}
|
||||
}
|
||||
|
||||
int Servo::read() // return the value as degrees
|
||||
{
|
||||
return (map(readMicroseconds()+1, this->min, this->max, 0, 180));
|
||||
}
|
||||
|
||||
int Servo::readMicroseconds()
|
||||
{
|
||||
int pulsewidthUsec;
|
||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached()))
|
||||
{
|
||||
pulsewidthUsec = ticksToUs(this->ticks);
|
||||
}
|
||||
else
|
||||
{
|
||||
pulsewidthUsec = 0;
|
||||
}
|
||||
|
||||
return (pulsewidthUsec);
|
||||
}
|
||||
|
||||
bool Servo::attached()
|
||||
{
|
||||
return (ChannelUsed[this->servoChannel]);
|
||||
}
|
||||
|
||||
void Servo::setTimerWidth(int value)
|
||||
{
|
||||
// only allow values between 16 and 20
|
||||
if (value < 16)
|
||||
value = 16;
|
||||
else if (value > 20)
|
||||
value = 20;
|
||||
|
||||
// Fix the current ticks value after timer width change
|
||||
// The user can reset the tick value with a write() or writeUs()
|
||||
int widthDifference = this->timer_width - value;
|
||||
// if positive multiply by diff; if neg, divide
|
||||
if (widthDifference > 0)
|
||||
{
|
||||
this->ticks << widthDifference;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ticks >> widthDifference;
|
||||
}
|
||||
|
||||
this->timer_width = value;
|
||||
this->timer_width_ticks = pow(2,this->timer_width);
|
||||
|
||||
// If this is an attached servo, clean up
|
||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached()))
|
||||
{
|
||||
// detach, setup and attach again to reflect new timer width
|
||||
ledcDetachPin(this->pinNumber);
|
||||
ledcSetup(this->servoChannel, REFRESH_CPS, this->timer_width);
|
||||
ledcAttachPin(this->pinNumber, this->servoChannel);
|
||||
}
|
||||
}
|
||||
|
||||
int Servo::readTimerWidth()
|
||||
{
|
||||
return (this->timer_width);
|
||||
}
|
||||
|
||||
int Servo::usToTicks(int usec)
|
||||
{
|
||||
return (int)((float)usec / ((float)REFRESH_USEC / (float)this->timer_width_ticks));
|
||||
}
|
||||
|
||||
int Servo::ticksToUs(int ticks)
|
||||
{
|
||||
return (int)((float)ticks * ((float)REFRESH_USEC / (float)this->timer_width_ticks));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user