#include "SoftwareServo.h" SoftwareServo *SoftwareServo::first; #define NO_ANGLE (0xff) SoftwareServo::SoftwareServo() : pin(0), angle(NO_ANGLE), pulse0(0), min16(34), max16(150), next(0) {} void SoftwareServo::setMinimumPulse(uint16_t t) { min16 = t/16; } void SoftwareServo::setMaximumPulse(uint16_t t) { max16 = t/16; } uint8_t SoftwareServo::attach(int pinArg) { pin = pinArg; angle = NO_ANGLE; pulse0 = 0; next = first; first = this; digitalWrite(pin, 0); pinMode(pin, OUTPUT); return 1; } void SoftwareServo::detach() { for (SoftwareServo **p=&first; *p!=0; p=&((*p)->next) ) { if (*p == this) { *p = this->next; this->next = 0; return; } } } void SoftwareServo::write(int angleArg) { if (angleArg < 0) angleArg = 0; if (angleArg > 180) angleArg = 180; angle = angleArg; // bleh, have to use longs to prevent overflow, could be tricky if always a 16MHz clock, but not true // That 64L on the end is the TCNT0 prescaler, it will need to change if the clock's prescaler changes, // but then there will likely be an overflow problem, so it will have to be handled by a human. pulse0 = (min16*16L*clockCyclesPerMicrosecond() + (max16-min16)*(16L*clockCyclesPerMicrosecond())*angle/180L)/64L; } uint8_t SoftwareServo::read() { return angle; } uint8_t SoftwareServo::attached() { for (SoftwareServo *p=first; p!=0; p=p->next ) { if (p == this) return 1; } return 0; } void SoftwareServo::refresh() { uint8_t count = 0, i = 0; uint16_t base = 0; SoftwareServo *p; static unsigned long lastRefresh = 0; unsigned long m = millis(); // if we haven't wrapped millis, and 20ms have not passed, then don't do anything if (m >= lastRefresh && m < lastRefresh + 20) return; lastRefresh = m; for (p=first; p!=0; p=p->next ) if (p->pulse0) count++; if (count == 0) return; // gather all the SoftwareServos in an array SoftwareServo *s[count]; for (p=first; p !=0; p=p->next ) if (p->pulse0) s[i++] = p; // bubblesort the SoftwareServos by pulse time, ascending order for(;;) { uint8_t moved = 0; for (i = 1; i < count; i++) { if (s[i]->pulse0 < s[i-1]->pulse0) { SoftwareServo *t = s[i]; s[i] = s[i-1]; s[i-1] = t; moved = 1; } } if (!moved) break; } // turn on all the pins // Note the timing error here... when you have many SoftwareServos going, the // ones at the front will get a pulse that is a few microseconds too long. // Figure about 4uS/SoftwareServo after them. This could be compensated, but I feel // it is within the margin of error of software SoftwareServos that could catch // an extra interrupt handler at any time. for (i=0; ipin, 1); uint8_t start = TCNT0; uint8_t now = start; uint8_t last = now; // Now wait for each pin's time in turn.. for (i=0; ipulse0; // loop until we reach or pass 'go' time for (;;) { now = TCNT0; if (now < last) base += 256; last = now; if (base+now > go) { digitalWrite(s[i]->pin, 0); break; } } } }