I need my ESP8266 to control a servo while using a DS18B20. The 1-Wire protocol used by DS18B20 occasionally disables interrupts, which causes servo jitter.
The Playground SoftwareServo library works with AVRs but doesn't work with the ESP8266.
I wrote my own "softwareServo" program, below, using the ESP8266's "ccount" register for timing. That register is incremented with every clock tick.
The code works (servo goes to the correct angles), but it has some jitter, too.
Any ideas on how to improve this code, or things to test? (I don't have a way of measuring the pulse timing.)
/*
esp8266 & servo, using software control
It works, but there is some jitter.
*/
uint32_t currentCount, startPulseCount, angleCount, previousTime;
const uint32_t twentyCount = 1600000; // 20x10^-3 x 80x10^6 counts 20.00 mS = 20000 uS
const uint32_t minCount = 68000; // 0.85x10^-3 x 80x10^6 counts 0.85 mS = 850 uS
const uint32_t maxCount = 188000; // 2.35x10^-3 x 80x10^6 counts 2.35 mS = 2350 uS
uint8_t myAng[4] = {18, 31, 135, 150}; // test servo angles
uint8_t myAngle, i;
bool pinIsHigh = false;
const byte pinForServo = 0; // was 16, but can't use GPOS or GPOC with 16. FWIW, setting 0 low turns on red LED.
static inline int32_t asm_ccount(void) { // gets contents of reg that is updated w/ every clock tick...80x10^6 ticks/s (if crystal is accurate)
int32_t r; // this asm function takes one count, vs ESP.getCycleCount() which takes 4 or 5
asm volatile ("rsr %0, ccount" : "=r"(r));
return r;
}
void setup() {
pinMode(pinForServo, OUTPUT);
}
void loop() {
//currentTime = millis(); // takes about 7 uS
if (millis() - previousTime >= 5000) { // change servo angle, for demo purposes
previousTime += 5000;
myAngle = myAng[i % 4]; // degrees
i++;
angleCount = minCount + myAngle * (maxCount - minCount) / 180;
}
currentCount = asm_ccount(); // takes one tick to execute, so should subtract one tick, or 1/80 uS... neglect
static uint32_t previous20count = currentCount + twentyCount; // just for 1st time thru
// set servo pin high every 20 ms (50 Hz)
if (currentCount - previous20count >= twentyCount) {
GPOS = (1 << pinForServo); // set pin high; takes 0.06 uS vs 1.5 uS for digitalWrite (fun, but hardly worth doing...)
previous20count += twentyCount;
startPulseCount = currentCount;
pinIsHigh = true;
}
// check to see if pulse time is over; if so, set pin low
if (pinIsHigh and currentCount - startPulseCount >= angleCount) {
GPOC = (1 << pinForServo); // clear pin (set low) after appropriate pulse width
pinIsHigh = false;
}
} // end of loop
Another solution would be to use a stand-alone servo controller like Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface [PCA9685] : ID 815 : $14.95 : Adafruit Industries, Unique & fun DIY electronics and kits
Overkill for one servo. Oh well.
Or just put up with the jitter.