Skip to content

src/psoc: Add psoc6 port for Servo Library. #141

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ sentence=Allows Arduino boards to control a variety of servo motors.
paragraph=This library can control a great number of servos.<br />It makes careful use of timers: the library can control 12 servos using only 1 timer.<br />On the Arduino Due you can control up to 60 servos.
category=Device Control
url=https://www.arduino.cc/reference/en/libraries/servo/
architectures=avr,megaavr,sam,samd,nrf52,stm32f4,mbed,mbed_nano,mbed_portenta,mbed_rp2040,renesas,renesas_portenta,renesas_uno
architectures=avr,megaavr,sam,samd,nrf52,stm32f4,mbed,mbed_nano,mbed_portenta,mbed_rp2040,renesas,renesas_portenta,renesas_uno,psoc6
6 changes: 4 additions & 2 deletions src/Servo.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@
#include "xmc/ServoTimers.h"
#elif defined(ARDUINO_ARCH_ESP32)
#include "esp32/ServoTimers.h"
#elif defined(ARDUINO_ARCH_PSOC6)
#include "psoc6/ServoTimers.h"
#else
#error "This library only supports boards with an AVR, SAM, SAMD, NRF52, STM32F4, Renesas, XMC or ESP32 processor."
#error "This library only supports boards with an AVR, SAM, SAMD, NRF52, STM32F4, Renesas, XMC, ESP32 or PSOC6 processor."
#endif

#define Servo_VERSION 2 // software version of this library
Expand All @@ -95,7 +97,7 @@

#define INVALID_SERVO 255 // flag indicating an invalid servo index

#if !defined(ARDUINO_ARCH_STM32F4) && !defined(ARDUINO_ARCH_XMC)
#if !defined(ARDUINO_ARCH_STM32F4) && !defined(ARDUINO_ARCH_XMC) &&!defined(ARDUINO_ARCH_PSOC6)

typedef struct {
uint8_t nbr :6 ; // a pin number from 0 to 63
Expand Down
180 changes: 180 additions & 0 deletions src/psoc6/Servo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/******************************************************************************
* The MIT License
*
* Copyright (c) 2010, LeafLabs, LLC.
*
* 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.
*****************************************************************************/

#if defined(ARDUINO_ARCH_PSOC6)

#include "ServoTimers.h"

uint8_t _ServoCount = 1; // internal counter to check if max numbers of servos is reached
static uint8_t _allowed[MAX_PWM_SERVOS] = ALLOWED_PINS; // internal array to check allowed pwm pins
static uint8_t _servos[MAX_PWM_SERVOS]; // static array of used servo pins for checking


/**
* @brief None blocking wait loop.
*
* @param uS microseconds to wait
*/
static void _delayUs(unsigned long uS)
{
unsigned long time_now = micros();
while (micros() < time_now + uS)
;
}

Servo::Servo()
{
if(_ServoCount <= MAX_PWM_SERVOS)
{
this->servoIndex = _ServoCount++;

// Initialize the servo with default values
this->_minAngle = MIN_ANGLE;
this->_maxAngle = MAX_ANGLE;
this->_minPW = MIN_PULSE_WIDTH;
this->_maxPW = MAX_PULSE_WIDTH;
this->_pin = 0;
this->_isActive = false;
this->_pwm = 0;
this->_deg = 0.0;

for(int i = 0; i < MAX_PWM_SERVOS; i++)
{
_servos[i] = 0xFF; // Initialize all servo pins to unused state
}
}
else
{
this->servoIndex = INVALID_SERVO; // No more servos can be attached
}
}

uint8_t Servo::attach(uint8_t pin, uint16_t min, uint16_t max)
{
if(this->servoIndex <= MAX_PWM_SERVOS)
{
// Validate the pin
bool pin_allowed = false;
for(int i = 0; i < MAX_PWM_SERVOS; i++)
{
// Check if pin already in use
if(_servos[i] == pin)
{
return INVALID_SERVO;
}
// Check if selected pin has a PWM unit on the used PSOC6 board
if(_allowed[i] == pin)
pin_allowed = true;
}

// Return if pin is not found in allowed pin list
if(!pin_allowed)
return INVALID_SERVO;

// Set min/max values according to input and check for absolute limits
if(min < MIN_PULSE_CHECK)
{
this->_minAngle = constrain(min, MIN_ANGLE, MAX_ANGLE);
this->_minPW = MIN_PULSE_WIDTH;
}
else
{
this->_minAngle = MIN_ANGLE; // TODO: has to be calculated
this->_minPW = constrain(min, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
}

if(max < MIN_PULSE_CHECK)
{
this->_maxAngle = constrain(max, MIN_ANGLE, MAX_ANGLE);
this->_maxPW = 2 * MAX_PULSE_WIDTH;
}
else
{
this->_maxAngle = MAX_ANGLE; // TODO: has to be calculated
this->_maxPW = constrain(max, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
}

this->_pin = pin;
this->_isActive = true;
_servos[this->servoIndex - 1] = pin; // Mark the pin as used
analogWriteResolution(RESOLUTION);
setAnalogWriteFrequency(this->_pin, REFRESH_FREQUENCY); // Set the default frequency for the pin

}
return this->servoIndex;
}

void Servo::detach()
{
this->servoIndex = _ServoCount--;

this->_minAngle = MIN_ANGLE;
this->_maxAngle = MAX_ANGLE;
this->_minPW = MIN_PULSE_WIDTH;
this->_maxPW = MAX_PULSE_WIDTH;

this->_pin = 0;
this->_isActive = false;
this->_pwm = 0;
this->_deg = 0.0;
_servos[this->servoIndex - 1] = 0xFF; // Mark the pin as unused
}

void Servo::write(int value)
{
if (value < MIN_PULSE_CHECK)
{
// angle must be inside the boundaries
double angle = constrain(value, this->_minAngle, this->_maxAngle);
double dutyCycle = ( 0.5 + ( angle / MAX_ANGLE ) * 2.0 ) * DUTYCYCLE_STEPS;

this->_deg = angle;
this->_pwm = uint16_t(dutyCycle);

analogWrite(this->_pin, uint16_t(dutyCycle));
_delayUs(50);
} else {
writeMicroseconds(value);
}
}

void Servo::writeMicroseconds(int value)
{
// value must be inside the boundaries
double pw = constrain(value,this->_minPW, this->_maxPW);
double dutyCycle = map(pw, MIN_PULSE_WIDTH,MAX_PULSE_WIDTH, 0.5 * DUTYCYCLE_STEPS, 2.5 * DUTYCYCLE_STEPS);

this->_deg = ( dutyCycle - DUTYCYCLE_STEPS * 0.5 ) * MAX_ANGLE / ( 2 * DUTYCYCLE_STEPS );
this->_pwm = uint16_t(dutyCycle);
analogWrite(this->_pin, uint16_t(dutyCycle));

_delayUs(50);
}
#endif




169 changes: 169 additions & 0 deletions src/psoc6/ServoTimers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/******************************************************************************
* The MIT License
*
* Copyright (c) 2010, LeafLabs, LLC.
*
* 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.
*****************************************************************************/

/*
* @copyright Copyright (c) 2025-2026 Infineon Technologies AG
*/

#ifndef __SERVO_TIMERS_H__
#define __SERVO_TIMERS_H__

#include <Arduino.h>

#define MAX_PWM_SERVOS 28
#define ALLOWED_PINS {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}


#define MIN_ANGLE 0 // the minimal angle in degree
#define MAX_ANGLE 180 // the maximal angle in degree
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo in microseconds
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo in microseconds

#define MIN_PULSE_CHECK 500 // border with below = angle and above = pulse width
#define REFRESH_FREQUENCY 50u // the refresh frequency on analog pins
#define REFRESH_TIME 20.0 // the PWM refresh frequency for the servo motor
#define DUTYCYCLE_STEPS 65536.0 / REFRESH_TIME // the number of duty cycle steps during one refresh period
#define RESOLUTION 16 // the resolution of the adc during analog write

#define INVALID_SERVO 255 // flag indicating an invalid servo index

/** Class for interfacing with RC servomotors. */
class Servo
{
public:
/**
* @brief Construct a new Servo instance.
*
* The new instance will not be attached to any pin, but only PWM capable pins will run.
* see pin list above.
*/
Servo();

/**
* @brief Associate this instance with a servomotor whose input is
* connected to pin.
*
* If this instance is already attached to a pin, it will be
* detached before being attached to the new pin.
* If the pin is not allowed for running PWM or the max number of
* PWM channels on the PSOC6 board is reached it will return
* with an INVALID_SERVO, otherwise with the servoIndex number.
*
* @param pin Pin connected to the servo pulse wave input. This
* pin must be capable of PWM output.
*
* @param min If this value is below MIN_PULSE_CHECK it will be associated
* with an angle in degree. Otherwise it will be the minimum
* pulse width.
* min as an angle must be between MIN_ANGLE < angle < MAX_ANGLE
* with default as MIN_ANGLE
* min as a pulse width must be between MIN_PULSE_WIDTH < pwm < MAX_PULSE_WIDTH
* with a default as MIN_PULSE_WIDTH
*
* @param max If this value is below MIN_PULSE_CHECK it will be associated
* with an angle in degree. Otherwise it will be the maximum
* pulse width.
* max as an angle must be between MIN_ANGLE < angle < MAX_ANGLE
* with default as MAX_ANGLE
* max as a pulse width must be between MIN_PULSE_WIDTH < pwm < MAX_PULSE_WIDTH
* with a default as MAX_PULSE_WIDTH
*
* @return servoIndex number or INVALID_SERVO = 255 in case of an error
*/
uint8_t attach(uint8_t pin, uint16_t min = MIN_ANGLE, uint16_t max = MAX_ANGLE);


/**
* @brief Stop driving the servo pulse train.
*
* If not currently attached to a motor, this function has no effect.
*
* @return true if this call did anything, false otherwise.
*/
void detach();

/**
* @brief Set the servomotor target angle by recalculating the duty cycle
* for PSOC6 PWM settings.
*
* @param value Target angle, in degrees. If the target angle is
* outside the range specified at attach(), it
* will be clamped to lie in that range.
*
* @see Servo::attach()
*/
void write(int value);

/**
* @brief Set the pulse width, in microseconds by recalculating it for the
* PSOC6 PWM settings. It also calculates the angle from the pwm value.
*
* @param value Pulse width to send to the servomotor, in
* microseconds. If outside of the range
* specified at attach() time, it is clamped to
* lie in that range.
*
* @see Servo::attach()
*/
void writeMicroseconds(int value);

/**
* returns the current value in degree as an angle between 0 and 189 degrees
*
* @see Servo::attach()
*/
int read() const { return uint16_t(this->_deg); }

/**
* returns the current pwm value in microseconds.
*
* @see Servo::attach()
*/
int readMicroseconds() const { return uint16_t(this->_pwm); }

/**
* @brief Check if this instance is attached to a servo.
* @return true if this instance is attached to a servo, false otherwise.
* @see Servo::attachedPin()
*/
bool attached() const { return this->_isActive; }

private:
uint16_t _minPW; // the initial minPulseWidth, if not set than MIN_PULSE_WIDTH
uint16_t _maxPW; // the initial maxPulseWidth, if not set than MAX_PULSE_WIDTH
int16_t _minAngle; // the initial minAngle, if not set than MIN_ANGLE
int16_t _maxAngle; // the initial maxAngle, if not set than MAX_ANGLE
int16_t _pin; // attached arduino pin number
double _deg; // actual angle in degree
double _pwm; // actual pwm signal in microseconds
uint8_t _isActive; // true if this pin is active, otherwise false

uint8_t servoIndex; // the actual number of Servos attached to this library


};

#endif