pxt-ev3/libs/core/output.ts

320 lines
9.3 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-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-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-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
*/
//% blockId=motorStopAll block="stop all motors"
//% weight=10 group="Motors" blockGap=8
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-10-27 10:47:25 +02:00
private port: Output;
private large: boolean;
private brake: boolean;
2017-10-27 09:09:00 +02:00
constructor(port: Output, large: boolean) {
2017-10-25 05:16:33 +02:00
super();
this.port = port;
2017-10-27 09:09:00 +02:00
this.large = large;
2017-10-27 10:47:25 +02:00
this.brake = false;
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-10-31 05:29:18 +01:00
* Sets the motor power level from ``-100`` to ``100``.
* @param motor the output connection that the motor is connected to
* @param power the power from ``100`` full forward to ``-100`` full backward, eg: 50
2017-10-25 05:16:33 +02:00
*/
2017-10-31 05:29:18 +01:00
//% blockId=motorSetPower block="power %motor|to %power|%"
2017-10-27 11:52:42 +02:00
//% weight=99 group="Motors" blockGap=8
2017-10-31 05:29:18 +01:00
//% power.min=-100 power.max=100
power(power: number) {
power = Math.clamp(-100, 100, power >> 0);
const b = mkCmd(this.port, DAL.opOutputPower, 1)
b.setNumber(NumberFormat.Int8LE, 2, power)
writePWM(b)
if (power) {
2017-10-27 10:47:25 +02:00
const b = mkCmd(this.port, DAL.opOutputStart, 0)
2017-10-27 11:52:42 +02:00
writePWM(b);
2017-10-27 10:47:25 +02:00
} else {
2017-10-31 05:29:18 +01:00
this.stop();
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-10-25 05:16:33 +02:00
/**
2017-10-31 05:29:18 +01:00
* Moves the motor by a number of degrees
* @param degrees the angle to turn the motor
* @param angle the degrees to rotate, eg: 360
* @param power the power from ``100`` full forward to ``-100`` full backward, eg: 50
2017-10-25 05:16:33 +02:00
*/
2017-10-31 05:29:18 +01:00
//% blockId=motorMove block="move %motor|by %angle|degrees at %power|%"
//% weight=98 group="Motors" blockGap=8
//% power.min=-100 power.max=100
move(angle: number, power: number) {
angle = angle >> 0;
power = Math.clamp(-100, 100, power >> 0);
step(this.port, {
speed: power,
step1: 0,
step2: angle,
step3: 0,
useSteps: true,
useBrake: this.brake
})
2017-10-25 05:16:33 +02:00
}
2017-10-31 05:29:18 +01:00
/**
* Stops the motor
*/
//% blockId=motorStop block="stop %motor"
//% weight=97 group="Motors"
stop() {
const b = mkCmd(this.port, DAL.opOutputStop, 1)
b.setNumber(NumberFormat.UInt8LE, 2, this.brake ? 1 : 0)
writePWM(b);
}
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-10-31 05:29:18 +01:00
//% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake"
2017-10-27 10:47:25 +02:00
//% brake.fieldEditor=toggleonoff
2017-10-27 11:52:42 +02:00
//% weight=60 group="Motors" blockGap=8
2017-10-27 10:47:25 +02:00
setBrake(brake: boolean) {
this.brake = brake;
}
2017-10-27 11:52:42 +02:00
/**
* Reverses the motor polarity
*/
2017-10-31 05:29:18 +01:00
//% blockId=motorSetReversed block="set %motor|reversed %reversed"
2017-10-27 11:52:42 +02:00
//% reversed.fieldEditor=toggleonoff
//% weight=59 group="Motors"
setReversed(reversed: boolean) {
const b = mkCmd(this.port, DAL.opOutputPolarity, 1)
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
*/
//% blockId=motorSpeed block="%motor|speed"
2017-10-31 05:29:18 +01:00
//% weight=72 group="Motors" blockGap=8
2017-10-27 11:52:42 +02:00
speed(): number {
2017-10-25 05:16:33 +02:00
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
*/
//% blockId=motorCount block="%motor|count"
2017-10-31 05:29:18 +01:00
//% weight=71 group="Motors" blockGap=8
2017-10-27 11:52:42 +02:00
count(): number {
return getMotorData(this.port).count;
}
/**
* Gets motor tacho count.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="%motor|tacho count"
2017-10-31 05:29:18 +01:00
//% weight=70 group="Motors"
2017-10-27 11:52:42 +02:00
tachoCount(): number {
return getMotorData(this.port).tachoCount;
}
/**
* Clears the motor count
*/
clearCount() {
const b = mkCmd(this.port, DAL.opOutputClearCount, 0)
writePWM(b)
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (this.port & (1 << i)) {
motorMM.setNumber(NumberFormat.Int32LE, i * MotorDataOff.Size + MotorDataOff.TachoSensor, 0)
}
}
}
/**
* Resets the motor.
*/
reset() {
reset(this.port);
}
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 {
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-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-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
}