Skip to content
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

Add-servo-superpin #62

Merged
merged 17 commits into from
Aug 1, 2023
3 changes: 2 additions & 1 deletion EX-IOExpander.ino
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,11 @@ void loop() {
processInputs();
outputTestState = processOutputTest(outputTestState);
processServos();
SuperPin::loop();
}
if (diag) {
displayPins();
}
processSerialInput();
processDisplayOutput();
}
}
125 changes: 125 additions & 0 deletions Servo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
Copyright (c) 2009 Michael Margolis. 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
*/

/*
A servo is activated by creating an instance of the Servo class passing
the desired pin to the attach() method.
The servos are pulsed in the background using the value most recently
written using the write() method.

Note that analogWrite of PWM on pins associated with the timer are
disabled when the first servo is attached.
Timers are seized as needed in groups of 12 servos - 24 servos use two
timers, 48 servos will use four.
The sequence used to seize timers is defined in timers.h

The methods are:

Servo - Class for manipulating servo motors connected to Arduino pins.

attach(pin ) - Attaches a servo motor to an I/O pin.
attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
default min is 544, max is 2400

write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
writeMicroseconds() - Sets the servo pulse width in microseconds
read() - Gets the last written servo pulse width as an angle between 0 and 180.
readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
attached() - Returns true if there is a servo attached.
detach() - Stops an attached servos from pulsing its I/O pin.
*/

#ifndef Servo_h
#define Servo_h

#include <inttypes.h>

/*
* Defines for 16 bit timers used with Servo library
*
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
* _Nbr_16timers indicates how many 16 bit timers are available.
*/

// Architecture specific include
#if defined(ARDUINO_ARCH_AVR)
#include "avr/ServoTimers.h"
#elif defined(ARDUINO_ARCH_SAM)
#include "sam/ServoTimers.h"
#elif defined(ARDUINO_ARCH_SAMD)
#include "samd/ServoTimers.h"
#elif defined(ARDUINO_ARCH_STM32F4)
#include "stm32f4/ServoTimers.h"
#elif defined(ARDUINO_ARCH_NRF52)
#include "nrf52/ServoTimers.h"
#elif defined(ARDUINO_ARCH_MEGAAVR)
#include "megaavr/ServoTimers.h"
#elif defined(ARDUINO_ARCH_MBED)
#include "mbed/ServoTimers.h"
#elif defined(ARDUINO_ARCH_RENESAS)
#include "renesas/ServoTimers.h"
#else
#error "This library only supports boards with an AVR, SAM, SAMD, NRF52 or STM32F4 processor."
#endif

#define Servo_VERSION 2 // software version of this library

#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
#define REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds

#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)

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

#if !defined(ARDUINO_ARCH_STM32F4)

typedef struct {
uint8_t nbr :6 ; // a pin number from 0 to 63
uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false
} ServoPin_t ;

typedef struct {
ServoPin_t Pin;
volatile unsigned int ticks;
} servo_t;

class Servo
{
public:
Servo();
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or INVALID_SERVO if failure
uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
void detach();
void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
void writeMicroseconds(int value); // Write pulse width in microseconds
int read(); // returns current pulse width as an angle between 0 and 180 degrees
int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
bool attached(); // return true if this servo is attached, otherwise false
private:
uint8_t servoIndex; // index into the channel data for this servo
int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
};

#endif
#endif
136 changes: 136 additions & 0 deletions SuperPin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* © 2023, Chris Harlow and Peter Cole. All rights reserved.
*
* This file is part of EX-IOExpander.
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/

#include <Arduino.h>
#include "SuperPin.h"

SuperPin* volatile SuperPin::firstPin=NULL;


// create a superpin for you to set
// e.g. SuperPin p=new SuperPin(15);
// then set the pattern when required with p->setPattern(....)

SuperPin::SuperPin(byte _pinId) {
pinId=_pinId;
onCount=0;
offCount=255;
runningCount=255;
pinMode(_pinId, OUTPUT);
pinState=LOW;
digitalWrite(pinId,pinState);

// chain in the new pin
noInterrupts();
next=firstPin;
firstPin=this;
interrupts();
}

// Call this to set a pattern of on/off
// setPattern(25,100) low frequency PWM 20% (25 on, 100 off)
// setPattern(1,4) high frequency PWM 20% (1 on, 4 off)
// and so on...

void SuperPin::setPattern(byte _onCount, byte _offCount) {
noInterrupts();
onCount=_onCount;
offCount=_offCount;
runningCount=0;
// pinState=LOW;
pinState=_offCount?LOW:HIGH;
fastDigitalWrite(pinId, pinState);
interrupts();
}

// Set a pin to be HIGH or LOW
void SuperPin::set(byte _pinId, bool _high) {
if (_high) setPattern(_pinId,255,0);
else setPattern(_pinId,0,255);
}

// Set a pin to be a PWM pattern
void SuperPin::setPattern(byte _pinId, byte _onCount, byte _offCount) {
for ( SuperPin* volatile p=firstPin; p; p=p->next){
if (p->pinId==_pinId) {
p->setPattern(_onCount, _offCount);
return;
}
}
(new SuperPin(_pinId))->setPattern(_onCount,_offCount);
}


void SuperPin::tick() {
if (runningCount) {
runningCount--;
return;
}
if (pinState) {
// pin is HIGH... switch to LOW unless locked
if (offCount==0) {
// pin is locked on
runningCount=onCount;
return;
}
runningCount=offCount;
pinState=LOW;
}
else {
// pin is LOW switch to HIGH unless locked
if (onCount==0) {
// pin is locked off
runningCount=offCount;
return;
}
runningCount=onCount;
pinState=HIGH;
}
fastDigitalWrite(pinId, pinState);
runningCount--;
}


// EITHER - call this loop() function at a suitable frequency from your
// sketch loop()
// OR use a timer of your choice call loop() at the frequency
// of your choice.
// e.g. Timer1.attachInterrupt(SuperPin::loop,freq);

void SuperPin::loop() {
for (SuperPin* volatile p=firstPin; p; p=p->next) p->tick();
}

// Fast digital writes stolen from EX-CommandStation
void SuperPin::fastDigitalWrite(uint8_t pin, bool state) {
#ifdef USE_FAST_WRITES
if (pin >= NUM_DIGITAL_PINS) return;
uint8_t mask = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *outPortAdr = portOutputRegister(port);
noInterrupts();
if (state)
*outPortAdr |= mask;
else
*outPortAdr &= ~mask;
interrupts();
#else
digitalWrite(pin, state);
#endif
}
43 changes: 43 additions & 0 deletions SuperPin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* © 2023, Chris Harlow and Peter Cole. All rights reserved.
*
* This file is part of EX-IOExpander.
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/

#ifndef SUPERPIN_H
#define SUPERPIN_H

#include <Arduino.h>
#include "defines.h"

class SuperPin {
public:
static void setPattern(byte pinId, byte _onCount, byte _offCount);
static void set(byte pinId, bool high);
static void loop();

private:
SuperPin(byte _pinid);
void setPattern(byte _onCount, byte _offCount);
void tick();
static SuperPin* volatile firstPin;
SuperPin* volatile next;
volatile byte pinId, onCount, offCount, runningCount;
volatile bool pinState;
static void fastDigitalWrite(uint8_t pin, bool state);
};

#endif
59 changes: 58 additions & 1 deletion arduino_avr_mega.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,61 @@ pinName pinNameMap[TOTAL_PINS] = {
{A12,"A12"},{A13,"A13"},{A14,"A14"},{A15,"A15"},
};

#endif
// Servo support here
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
Servo servo5;
Servo servo6;
Servo servo7;
Servo servo8;
Servo servo9;
Servo servo10;
Servo servo11;
Servo servo12;
Servo servo13;
Servo servo14;
Servo servo15;
Servo servo16;
Servo servo17;
Servo servo18;
Servo servo19;
Servo servo20;
Servo servo21;
Servo servo22;
Servo servo23;
Servo servo24;
Servo servo25;
Servo servo26;
Servo servo27;
Servo servo28;
Servo servo29;
Servo servo30;
Servo servo31;
Servo servo32;
Servo servo33;
Servo servo34;
Servo servo35;
Servo servo36;
Servo servo37;
Servo servo38;
Servo servo39;
Servo servo40;
Servo servo41;
Servo servo42;
Servo servo43;
Servo servo44;
Servo servo45;
Servo servo46;
Servo servo47;
Servo servo48;

Servo servoMap[MAX_SERVOS] = {
servo1,servo2,servo3,servo4,servo5,servo6,servo7,servo8,servo9,servo10,servo11,servo12,
servo13,servo14,servo15,servo16,servo17,servo18,servo19,servo20,servo21,servo22,servo23,servo24,
servo25,servo26,servo27,servo28,servo29,servo30,servo31,servo32,servo33,servo34,servo35,servo36,
servo37,servo38,servo39,servo40,servo41,servo42,servo43,servo44,servo45,servo46,servo47,servo48,
};

#endif
Loading
Loading