Continuous servo

* handle continuous state

* adding shims

* update rendering for continuous servos

* fixing sim

* fix sig

* typo

* fix sim

* bump pxt

* bump pxt
This commit is contained in:
Amerlander 2020-02-15 16:16:30 +01:00
parent 90ca4704bd
commit 34a651b7b1
7 changed files with 62 additions and 12 deletions

View File

@ -535,6 +535,7 @@
"pins.pulseIn": "Return the duration of a pulse at a pin in microseconds.",
"pins.pulseIn|param|name": "the pin which measures the pulse, eg: DigitalPin.P0",
"pins.pulseIn|param|value": "the value of the pulse, eg: PulseValue.High",
"pins.servoSetContinuous": "Specifies that a continuous servo is connected.",
"pins.servoSetPulse": "Configure the IO pin as an analog/pwm output and set a pulse width. The period is 20 ms period and the pulse width is set based on the value given in **microseconds** or `1/1000` milliseconds.",
"pins.servoSetPulse|param|micros": "pulse duration in micro seconds, eg:1500",
"pins.servoSetPulse|param|name": "pin name",

View File

@ -258,6 +258,14 @@ namespace pins {
PINOP(setServoValue(value));
}
/**
* Specifies that a continuous servo is connected.
*/
//%
void servoSetContinuous(AnalogPin name, bool value) {
// handled in simulator
}
/**
* Configure the IO pin as an analog/pwm output and set a pulse width. The period is 20 ms period and the pulse width is set based on the value given in **microseconds** or `1/1000` milliseconds.
* @param name pin name

View File

@ -48,6 +48,9 @@ interface PwmOnlyPin extends DigitalInOutPin, AnalogOutPin {
//% parts=microservo trackArgs=0
servoSetPulse(duration: number): void;
//% parts=microservo tracArgs=0
servoSetContinuous(value: boolean): void;
}
//% noRefCounting fixedInstances
@ -109,6 +112,10 @@ class MicrobitPin implements AnalogInPin, AnalogOutPin, AnalogInOutPin, PwmOnlyP
pins.servoWritePin(this.analogId(), value);
}
servoSetContinuous(value: boolean): void {
pins.servoSetContinuous(this.analogId(), value);
}
servoSetPulse(duration: number): void {
pins.servoSetPulse(this.analogId(), duration);
}

View File

@ -732,6 +732,12 @@ declare namespace pins {
//% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" shim=pins::servoWritePin
function servoWritePin(name: AnalogPin, value: int32): void;
/**
* Specifies that a continuous servo is connected.
*/
//% shim=pins::servoSetContinuous
function servoSetContinuous(name: AnalogPin, value: boolean): void;
/**
* Configure the IO pin as an analog/pwm output and set a pulse width. The period is 20 ms period and the pulse width is set based on the value given in **microseconds** or `1/1000` milliseconds.
* @param name pin name

View File

@ -81,6 +81,13 @@ namespace pxsim.pins {
pin.servoAngle = value;
}
export function servoSetContinuous(pinId: number, value: boolean) {
let pin = getPin(pinId);
if (!pin) return;
pin.servoSetContinuous(value);
}
export function servoSetPulse(pinId: number, micros: number) {
let pin = getPin(pinId);
if (!pin) return;

View File

@ -17,6 +17,7 @@ namespace pxsim {
mode = PinFlags.Unused;
pitch = false;
pull = 0; // PullDown
servoContinuous = false;
digitalReadPin(): number {
this.mode = PinFlags.Digital | PinFlags.Input;
@ -59,6 +60,10 @@ namespace pxsim {
runtime.queueDisplayUpdate();
}
servoSetContinuous(value: boolean) {
this.servoContinuous = !!value;
}
servoSetPulse(pinId: number, micros: number) {
// TODO
}

View File

@ -10,7 +10,7 @@ namespace pxsim.visuals {
<path id="VCC" fill="red" stroke-width="2" d="M53.72 21.93h5.504v22.627H53.72z"/>
<path id="LOGIC" fill="#fc0" stroke-width="2" d="M47.3 21.93h5.503v22.627H47.3z"/>
<path id="GND" fill="#a02c2c" stroke-width="2" d="M60.14 21.93h5.505v22.627H60.14z"/>
<path id="connector" fill="#111" stroke-width="2" d="M45.064 0a1.488 1.488 0 0 0-1.488 1.488v24.5a1.488 1.488 0 0 0 1.488 1.487h22.71a1.488 1.488 0 0 0 1.49-1.488v-24.5A1.488 1.488 0 0 0 67.774 0h-22.71z"/>
<path id="connector" stroke-width="2" d="M45.064 0a1.488 1.488 0 0 0-1.488 1.488v24.5a1.488 1.488 0 0 0 1.488 1.487h22.71a1.488 1.488 0 0 0 1.49-1.488v-24.5A1.488 1.488 0 0 0 67.774 0h-22.71z"/>
<g id="crank" transform="translate(0 -752.688)">
<path id="arm" fill="#ececec" stroke="#000" stroke-width="1.372" d="M47.767 880.88c-4.447 1.162-8.412 8.278-8.412 18.492s3.77 18.312 8.412 18.494c8.024.314 78.496 5.06 78.51-16.952.012-22.013-74.377-21.117-78.51-20.035z"/>
<circle id="path8216" cx="56.661" cy="899.475" r="8.972" fill="gray" stroke-width="2"/>
@ -24,6 +24,7 @@ namespace pxsim.visuals {
return { el: createMicroServoElement(), x: xy[0], y: xy[1], w: 112.188, h: 299.674 };
}
const SPEED = 300; // 0.1s/60 degree
export class MicroServoView implements IBoardPart<EdgeConnectorState> {
public style: string = "";
public overElement: SVGElement = undefined;
@ -61,21 +62,36 @@ namespace pxsim.visuals {
translateEl(this.element, [x, y])
}
updateState(): void {
this.targetAngle = 180.0 - this.state.getPin(this.pin).servoAngle;
if (this.targetAngle != this.currentAngle) {
const p = this.state.getPin(this.pin);
const continuous = !!p.servoContinuous;
const servoAngle = p.servoAngle;
if (continuous) {
// for a continuous servo, the angle is interpreted as a rotation speed
// 0 -> -100%, 90 - 0%, 180 - 100%
const now = U.now();
const dt = Math.min(now - this.lastAngleTime, 50) / 1000;
this.currentAngle = this.targetAngle;
this.targetAngle += ((servoAngle - 90) / 90) * SPEED * dt;
} else {
this.targetAngle = 180.0 - servoAngle;
}
if (this.targetAngle != this.currentAngle)
this.renderAngle();
}
private renderAngle() {
const now = U.now();
const cx = 56.661;
const cy = 899.475;
const speed = 300; // 0.1s/60 degree
const dt = Math.min(now - this.lastAngleTime, 50) / 1000;
const delta = this.targetAngle - this.currentAngle;
this.currentAngle += Math.min(Math.abs(delta), speed * dt) * (delta > 0 ? 1 : -1);
this.currentAngle += Math.min(Math.abs(delta), SPEED * dt) * (delta > 0 ? 1 : -1);
this.crankEl.setAttribute("transform", this.crankTransform
+ ` rotate(${this.currentAngle}, ${cx}, ${cy})`)
this.lastAngleTime = now;
setTimeout(() => runtime.updateDisplay(), 20);
}
}
updateTheme(): void {
}