pxt-ev3/libs/core/output.ts

463 lines
14 KiB
TypeScript
Raw Normal View History

2017-07-07 16:15:36 +02:00
enum Output {
2017-10-25 05:16:33 +02:00
//% block="A"
2017-07-07 16:15:36 +02:00
A = 0x01,
2017-10-25 05:16:33 +02:00
//% block="B"
2017-07-07 16:15:36 +02:00
B = 0x02,
2017-10-25 05:16:33 +02:00
//% block="C"
2017-07-07 16:15:36 +02:00
C = 0x04,
2017-10-25 05:16:33 +02:00
//% block="D"
2017-07-07 16:15:36 +02:00
D = 0x08,
2017-12-12 23:08:45 +01:00
//% block="B+C"
BC = 0x06,
2017-10-25 05:16:33 +02:00
//% block="All"
2017-07-07 16:15:36 +02:00
ALL = 0x0f
}
enum OutputType {
None = 0,
Tacho = 7,
MiniTacho = 8,
}
2017-12-14 07:40:40 +01:00
enum MoveUnit {
//% block="rotations"
Rotations,
//% block="degrees"
Degrees,
//% block="seconds"
Seconds
}
2017-10-27 20:01:11 +02:00
namespace motors {
let pwmMM: MMap
let motorMM: MMap
const enum MotorDataOff {
TachoCounts = 0, // int32
Speed = 4, // int8
Padding = 5, // int8[3]
TachoSensor = 8, // int32
Size = 12
}
function init() {
if (pwmMM) return
pwmMM = control.mmap("/dev/lms_pwm", 0, 0)
if (!pwmMM) control.fail("no PWM file")
motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
2017-12-13 23:55:14 +01:00
if (!motorMM) control.fail("no motor file")
2017-10-25 05:28:31 +02:00
resetMotors()
let buf = output.createBuffer(1)
buf[0] = DAL.opProgramStart
writePWM(buf)
}
2017-10-27 09:09:00 +02:00
function writePWM(buf: Buffer): void {
init()
pwmMM.write(buf)
}
2017-07-07 16:15:36 +02:00
2017-08-08 02:39:37 +02:00
function readPWM(buf: Buffer): void {
init()
pwmMM.read(buf);
}
2017-07-07 16:15:36 +02:00
function mkCmd(out: Output, cmd: number, addSize: number) {
2017-10-27 20:01:11 +02:00
const b = output.createBuffer(2 + addSize)
2017-07-07 16:15:36 +02:00
b.setNumber(NumberFormat.UInt8LE, 0, cmd)
b.setNumber(NumberFormat.UInt8LE, 1, out)
return b
}
2017-10-25 05:28:31 +02:00
function resetMotors() {
reset(Output.ALL)
2017-10-27 09:09:00 +02:00
}
2017-10-25 05:16:33 +02:00
2017-10-27 11:52:42 +02:00
/**
* Stops all motors
*/
2017-12-14 22:07:10 +01:00
//% blockId=motorStopAll block="stop all motors"
//% weight=19
2017-10-27 11:52:42 +02:00
export function stopAllMotors() {
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
writePWM(b)
}
2017-10-25 05:16:33 +02:00
//% fixedInstances
export class Motor extends control.Component {
2017-12-14 07:40:40 +01:00
private _port: Output;
private _large: boolean;
private _initialized: boolean;
private _brake: boolean;
private _follower: Motor; //
2017-10-27 10:47:25 +02:00
2017-10-27 09:09:00 +02:00
constructor(port: Output, large: boolean) {
2017-10-25 05:16:33 +02:00
super();
2017-12-14 07:40:40 +01:00
this._port = port;
this._large = large;
this._brake = false;
}
private __init() {
if (!this._initialized) {
// specify motor size on this port
const b = mkCmd(this._port, DAL.opOutputSetType, 1)
b.setNumber(NumberFormat.Int8LE, 2, this._large ? 0x07 : 0x08)
writePWM(b)
}
2017-10-25 05:16:33 +02:00
}
2017-08-08 02:39:37 +02:00
2017-10-25 05:16:33 +02:00
/**
2017-12-14 07:54:08 +01:00
* Sets the speed of the motor.
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
2017-10-25 05:16:33 +02:00
*/
2017-12-14 07:54:08 +01:00
//% blockId=motorPower block="set speed of `icons.motorLarge` %motor|to %speed|%"
2017-12-14 07:40:40 +01:00
//% on.fieldEditor=toggleonoff
//% weight=99 blockGap=8
2017-12-14 07:54:08 +01:00
//% speed.min=-100 speed.max=100
setSpeed(speed: number) {
this.__init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) { // always stop
2017-10-31 05:29:18 +01:00
this.stop();
2017-12-14 07:40:40 +01:00
} else {
2017-12-14 07:54:08 +01:00
if (this._follower) this.setSpeedSync(speed);
else this.setSpeedSingle(speed);
2017-10-27 10:47:25 +02:00
}
2017-10-25 05:16:33 +02:00
}
2017-07-07 16:15:36 +02:00
2017-12-14 07:40:40 +01:00
private setSpeedSingle(speed: number) {
const b = mkCmd(this._port, DAL.opOutputSpeed, 1)
b.setNumber(NumberFormat.Int8LE, 2, speed)
writePWM(b)
}
private setSpeedSync(speed: number) {
const out = this._port | this._follower._port;
syncMotors(out, {
speed: speed,
turnRatio: 0,
useBrake: !!this._brake
})
}
2017-10-25 05:16:33 +02:00
/**
2017-12-14 07:40:40 +01:00
* Moves the motor by a number of rotations, degress or seconds
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
2017-12-12 01:07:46 +01:00
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
2017-10-25 05:16:33 +02:00
*/
2017-12-14 07:54:08 +01:00
//% blockId=motorMove block="move `icons.motorLarge` %motor|for %value|%unit|at %speed|%"
2017-12-14 07:40:40 +01:00
//% weight=98
//% speed.min=-100 speed.max=100
2017-12-14 07:54:08 +01:00
move(value: number, unit: MoveUnit, speed: number) {
this.output(value, unit, speed, 0);
2017-12-14 07:40:40 +01:00
}
2017-12-14 07:54:08 +01:00
private output(value: number, unit: MoveUnit, speed: number, turnRatio: number) {
2017-12-14 07:40:40 +01:00
this.__init();
2017-12-14 07:54:08 +01:00
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
2017-12-14 07:40:40 +01:00
this.stop();
return;
}
turnRatio = Math.clamp(-200, 200, turnRatio >> 0);
let useSteps: boolean;
let stepsOrTime: number;
switch (unit) {
case MoveUnit.Rotations:
stepsOrTime = (value * 360) >> 0;
useSteps = true;
break;
case MoveUnit.Degrees:
stepsOrTime = value >> 0;
useSteps = true;
break;
default:
stepsOrTime = value;
useSteps = false;
break;
}
if (this._follower) {
syncMotors(this._port | this._follower._port, {
useSteps: useSteps,
stepsOrTime: stepsOrTime,
2017-12-14 07:54:08 +01:00
speed: speed,
2017-12-14 07:40:40 +01:00
turnRatio: turnRatio,
useBrake: this._brake
})
} else {
step(this._port, {
useSteps: useSteps,
step1: 0,
step2: stepsOrTime,
step3: 0,
2017-12-14 07:54:08 +01:00
speed: speed,
2017-12-14 07:40:40 +01:00
useBrake: this._brake
})
}
2017-10-25 05:16:33 +02:00
}
2017-10-31 05:29:18 +01:00
/**
* Stops the motor
*/
2017-12-14 07:40:40 +01:00
private stop() {
this.__init();
if (this._follower) stop(this._port | this._follower._port);
else stop(this._port);
}
2017-10-31 05:29:18 +01:00
2017-10-27 10:47:25 +02:00
/**
* Sets the automatic brake on or off when the motor is off
* @param brake a value indicating if the motor should break when off
*/
2017-11-16 22:05:50 +01:00
//% blockId=outputMotorSetBrakeMode block="set `icons.motorLarge` %motor|brake %brake"
2017-10-27 10:47:25 +02:00
//% brake.fieldEditor=toggleonoff
2017-12-14 07:40:40 +01:00
//% weight=60 blockGap=8
2017-10-27 10:47:25 +02:00
setBrake(brake: boolean) {
2017-12-14 07:54:08 +01:00
this.__init();
2017-12-14 07:40:40 +01:00
this._brake = brake;
2017-10-27 10:47:25 +02:00
}
2017-10-27 11:52:42 +02:00
/**
* Reverses the motor polarity
*/
2017-11-16 22:05:50 +01:00
//% blockId=motorSetReversed block="set `icons.motorLarge` %motor|reversed %reversed"
2017-10-27 11:52:42 +02:00
//% reversed.fieldEditor=toggleonoff
2017-12-14 07:40:40 +01:00
//% weight=59
2017-10-27 11:52:42 +02:00
setReversed(reversed: boolean) {
2017-12-14 07:40:40 +01:00
this.__init();
const b = mkCmd(this._port, DAL.opOutputPolarity, 1)
2017-10-27 11:52:42 +02:00
b.setNumber(NumberFormat.Int8LE, 2, reversed ? -1 : 1);
writePWM(b)
}
2017-10-25 05:16:33 +02:00
/**
* Gets motor actual speed.
* @param motor the port which connects to the motor
*/
2017-11-16 22:05:50 +01:00
//% blockId=motorSpeed block="`icons.motorLarge` %motor|speed"
2017-12-14 07:40:40 +01:00
//% weight=72 blockGap=8
2017-10-27 11:52:42 +02:00
speed(): number {
2017-12-14 07:40:40 +01:00
this.__init();
return getMotorData(this._port).actualSpeed;
2017-10-27 09:09:00 +02:00
}
2017-10-27 11:52:42 +02:00
/**
* Gets motor step count.
* @param motor the port which connects to the motor
*/
2017-11-16 22:05:50 +01:00
//% blockId=motorCount block="`icons.motorLarge` %motor|count"
2017-12-14 07:40:40 +01:00
//% weight=71 blockGap=8
2017-10-27 11:52:42 +02:00
count(): number {
2017-12-14 07:40:40 +01:00
this.__init();
return getMotorData(this._port).count;
2017-10-27 11:52:42 +02:00
}
/**
* Gets motor tacho count.
* @param motor the port which connects to the motor
*/
2017-11-16 22:05:50 +01:00
//% blockId=motorTachoCount block="`icons.motorLarge` %motor|tacho count"
2017-12-14 07:40:40 +01:00
//% weight=70
2017-10-27 11:52:42 +02:00
tachoCount(): number {
2017-12-14 07:40:40 +01:00
this.__init();
return getMotorData(this._port).tachoCount;
2017-10-27 11:52:42 +02:00
}
/**
* Clears the motor count
*/
2017-12-14 22:07:10 +01:00
//%
2017-10-27 11:52:42 +02:00
clearCount() {
2017-12-14 07:40:40 +01:00
this.__init();
const b = mkCmd(this._port, DAL.opOutputClearCount, 0)
2017-10-27 11:52:42 +02:00
writePWM(b)
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
2017-12-14 07:40:40 +01:00
if (this._port & (1 << i)) {
2017-10-27 11:52:42 +02:00
motorMM.setNumber(NumberFormat.Int32LE, i * MotorDataOff.Size + MotorDataOff.TachoSensor, 0)
}
}
}
/**
2017-12-14 07:40:40 +01:00
* Resets the motor and clears any synchronization
2017-10-27 11:52:42 +02:00
*/
2017-12-14 07:40:40 +01:00
//% blockId=motorReset block="reset `icons.motorLarge` %motor"
2017-12-14 22:07:10 +01:00
//% weight=20
2017-10-27 11:52:42 +02:00
reset() {
2017-12-14 07:40:40 +01:00
this.__init();
reset(this._port);
delete this._follower;
2017-12-14 22:07:10 +01:00
}
2017-12-14 07:40:40 +01:00
/**
* Synchronizes a follower motor to this motor
* @param motor the leader motor, eg: motors.largeB
* @param follower the motor that follows this motor commands, eg: motors.largeC
*/
//% blockId=motorSync block="sync `icons.motorLarge` %motor|with `icons.motorLarge` %follower"
//% weight=10 blockGap=8
sync(follower: Motor) {
this.__init();
if (this == follower) return; // can't sync with self
this._follower = follower;
2017-10-27 11:52:42 +02:00
}
2017-12-14 07:40:40 +01:00
/**
* Turns the motor and the follower motor by a number of rotations
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
2017-12-14 08:00:37 +01:00
* @param turnRatio the ratio of power sent to the follower motor, from ``-200`` to ``200``
2017-12-14 07:40:40 +01:00
*/
2017-12-14 07:54:08 +01:00
//% blockId=motorTurn block="turn `icons.motorLarge` %motor|by %value|%unit|at %speed|% turn %turnRadio"
2017-12-14 07:40:40 +01:00
//% weight=9 blockGap=8
//% turnRatio.min=-200 turnRatio=200
2017-12-14 22:07:10 +01:00
//% inlineInputMode=inline
2017-12-14 07:54:08 +01:00
turn(value: number, unit: MoveUnit, speed: number, turnRatio: number) {
this.output(value, unit, speed, turnRatio);
2017-12-14 07:40:40 +01:00
}
2017-07-07 16:15:36 +02:00
}
2017-11-16 21:58:37 +01:00
//% whenUsed fixedInstance block="large A"
2017-10-27 09:09:00 +02:00
export const largeMotorA = new Motor(Output.A, true);
2017-11-16 21:58:37 +01:00
//% whenUsed fixedInstance block="large B"
2017-10-27 09:09:00 +02:00
export const largeMotorB = new Motor(Output.B, true);
2017-11-16 21:58:37 +01:00
//% whenUsed fixedInstance block="large C"
2017-10-27 09:09:00 +02:00
export const largeMotorC = new Motor(Output.C, true);
2017-11-16 21:58:37 +01:00
//% whenUsed fixedInstance block="large D"
2017-10-27 09:09:00 +02:00
export const largeMotorD = new Motor(Output.D, true);
2017-11-16 21:58:37 +01:00
//% whenUsed fixedInstance block="medium A"
2017-10-27 09:09:00 +02:00
export const mediumMotorA = new Motor(Output.A, false);
2017-10-25 05:16:33 +02:00
2017-11-16 21:58:37 +01:00
//% whenUsed fixedInstance block="medium B"
2017-10-27 09:09:00 +02:00
export const mediumMotorB = new Motor(Output.B, false);
2017-10-25 05:16:33 +02:00
2017-11-16 21:58:37 +01:00
//% whenUsed fixedInstance block="medium C"
2017-10-27 09:09:00 +02:00
export const mediumMotorC = new Motor(Output.C, false);
2017-10-25 05:16:33 +02:00
2017-11-16 21:58:37 +01:00
//% whenUsed fixedInstance block="medium D"
2017-10-27 09:09:00 +02:00
export const mediumMotorD = new Motor(Output.D, false);
2017-10-25 05:16:33 +02:00
function reset(out: Output) {
let b = mkCmd(out, DAL.opOutputReset, 0)
2017-07-07 16:15:36 +02:00
writePWM(b)
}
function outOffset(out: Output) {
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (out & (1 << i))
return i * MotorDataOff.Size
}
return 0
}
2017-10-25 05:16:33 +02:00
interface MotorData {
actualSpeed: number; // -100..+100
tachoCount: number;
count: number;
}
// only a single output at a time
2017-10-25 05:16:33 +02:00
function getMotorData(out: Output): MotorData {
2017-12-13 23:55:14 +01:00
init()
let buf = motorMM.slice(outOffset(out), MotorDataOff.Size)
return {
actualSpeed: buf.getNumber(NumberFormat.Int8LE, MotorDataOff.Speed),
tachoCount: buf.getNumber(NumberFormat.Int32LE, MotorDataOff.TachoCounts),
count: buf.getNumber(NumberFormat.Int32LE, MotorDataOff.TachoSensor),
}
}
2017-08-08 02:39:37 +02:00
2017-12-12 22:20:25 +01:00
interface SyncOptions {
useSteps?: boolean;
speed: number;
turnRatio: number;
2017-12-14 07:40:40 +01:00
stepsOrTime?: number;
2017-12-12 22:20:25 +01:00
useBrake?: boolean;
}
function syncMotors(out: Output, opts: SyncOptions) {
const cmd = opts.useSteps ? DAL.opOutputStepSync : DAL.opOutputTimeSync;
const b = mkCmd(out, cmd, 11);
const speed = Math.clamp(-100, 100, opts.speed);
const turnRatio = Math.clamp(-200, 200, opts.turnRatio);
b.setNumber(NumberFormat.Int8LE, 2, speed)
// note that b[3] is padding
b.setNumber(NumberFormat.Int16LE, 4 + 4 * 0, turnRatio)
// b[6], b[7] is padding
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 1, opts.stepsOrTime || 0)
b.setNumber(NumberFormat.Int8LE, 4 + 4 * 2, opts.useBrake ? 1 : 0)
writePWM(b)
}
2017-10-25 05:16:33 +02:00
interface StepOptions {
2017-07-07 16:15:36 +02:00
power?: number;
speed?: number; // either speed or power has to be present
step1: number;
step2: number;
step3: number;
useSteps?: boolean; // otherwise use milliseconds
2017-08-08 02:39:37 +02:00
useBrake?: boolean;
2017-07-07 16:15:36 +02:00
}
2017-12-12 23:08:45 +01:00
function start(out: Output) {
const b = mkCmd(out, DAL.opOutputStart, 0)
writePWM(b);
}
2017-12-14 07:40:40 +01:00
function stop(out: Output) {
const b = mkCmd(out, DAL.opOutputStop, 1)
b.setNumber(NumberFormat.UInt8LE, 2, this.brake ? 1 : 0)
writePWM(b);
}
2017-10-25 05:16:33 +02:00
function step(out: Output, opts: StepOptions) {
let op = opts.useSteps ? DAL.opOutputStepSpeed : DAL.opOutputTimeSpeed
2017-07-07 16:15:36 +02:00
let speed = opts.speed
if (speed == null) {
speed = opts.power
op = opts.useSteps ? DAL.opOutputStepPower : DAL.opOutputTimePower
2017-07-07 16:15:36 +02:00
if (speed == null)
return
}
speed = Math.clamp(-100, 100, speed)
let b = mkCmd(out, op, 15)
b.setNumber(NumberFormat.Int8LE, 2, speed)
// note that b[3] is padding
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 0, opts.step1)
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 1, opts.step2)
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 2, opts.step3)
2017-08-08 02:39:37 +02:00
b.setNumber(NumberFormat.Int8LE, 4 + 4 * 3, opts.useBrake ? 1 : 0)
2017-07-07 16:15:36 +02:00
writePWM(b)
}
const types = [0, 0, 0, 0]
export function setType(out: Output, type: OutputType) {
let b = mkCmd(out, DAL.opOutputSetType, 3)
2017-07-07 16:15:36 +02:00
for (let i = 0; i < 4; ++i) {
if (out & (1 << i)) {
types[i] = type
}
b.setNumber(NumberFormat.UInt8LE, i + 1, types[i])
}
writePWM(b)
}
2017-07-10 10:10:36 +02:00
}
interface Buffer {
[index: number]: number;
// rest defined in buffer.cpp
2017-07-07 16:15:36 +02:00
}