2017-07-07 16:15:36 +02:00
|
|
|
enum Output {
|
|
|
|
A = 0x01,
|
|
|
|
B = 0x02,
|
|
|
|
C = 0x04,
|
|
|
|
D = 0x08,
|
|
|
|
ALL = 0x0f
|
|
|
|
}
|
|
|
|
|
|
|
|
enum OutputType {
|
|
|
|
None = 0,
|
|
|
|
Tacho = 7,
|
|
|
|
MiniTacho = 8,
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace output {
|
2017-07-11 16:58:51 +02:00
|
|
|
let pwmMM: MMap
|
|
|
|
let motorMM: MMap
|
2017-08-09 19:02:58 +02:00
|
|
|
let currentSpeed: number[] = []
|
2017-07-11 16:58:51 +02:00
|
|
|
|
|
|
|
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-08-08 11:41:47 +02:00
|
|
|
|
2017-07-11 16:58:51 +02:00
|
|
|
stop(Output.ALL)
|
|
|
|
|
2017-08-09 19:02:58 +02:00
|
|
|
currentSpeed[Output.A] = -1;
|
|
|
|
currentSpeed[Output.B] = -1;
|
|
|
|
currentSpeed[Output.C] = -1;
|
|
|
|
currentSpeed[Output.D] = -1;
|
|
|
|
currentSpeed[Output.ALL] = -1;
|
|
|
|
|
2017-07-11 16:58:51 +02:00
|
|
|
let buf = output.createBuffer(1)
|
|
|
|
buf[0] = DAL.opProgramStart
|
|
|
|
writePWM(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
let b = createBuffer(2 + addSize)
|
|
|
|
b.setNumber(NumberFormat.UInt8LE, 0, cmd)
|
|
|
|
b.setNumber(NumberFormat.UInt8LE, 1, out)
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2017-08-08 02:39:37 +02:00
|
|
|
/**
|
|
|
|
* Turn a motor on for a specified number of milliseconds.
|
|
|
|
* @param out the output connection that the motor is connected to
|
|
|
|
* @param ms the number of milliseconds to turn the motor on, eg: 500
|
|
|
|
* @param useBrake whether or not to use the brake, defaults to false
|
|
|
|
*/
|
2017-08-08 22:02:11 +02:00
|
|
|
//% blockId=output_turn block="turn motor %out| on for %ms|milliseconds"
|
2017-08-08 02:39:37 +02:00
|
|
|
//% weight=100 group="Motors"
|
|
|
|
export function turn(out: Output, ms: number, useBrake = false) {
|
|
|
|
// TODO: use current power / speed configuration
|
|
|
|
output.step(out, {
|
2017-08-09 19:02:58 +02:00
|
|
|
speed: 100,
|
2017-08-08 02:39:37 +02:00
|
|
|
step1: 0,
|
|
|
|
step2: ms,
|
|
|
|
step3: 0,
|
2017-08-09 19:02:58 +02:00
|
|
|
useSteps: false,
|
2017-08-08 02:39:37 +02:00
|
|
|
useBrake: useBrake
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Turn motor off.
|
|
|
|
* @param out the output connection that the motor is connected to
|
|
|
|
*/
|
2017-08-08 22:02:11 +02:00
|
|
|
//% blockId=output_stop block="turn motor %out|off"
|
2017-08-08 02:39:37 +02:00
|
|
|
//% weight=90 group="Motors"
|
|
|
|
export function stop(out: Output, useBrake = false) {
|
2017-07-11 16:18:59 +02:00
|
|
|
let b = mkCmd(out, DAL.opOutputStop, 1)
|
2017-08-08 02:39:37 +02:00
|
|
|
b.setNumber(NumberFormat.UInt8LE, 2, useBrake ? 1 : 0)
|
2017-07-07 16:15:36 +02:00
|
|
|
writePWM(b)
|
|
|
|
}
|
|
|
|
|
2017-08-08 02:39:37 +02:00
|
|
|
/**
|
|
|
|
* Turn motor on.
|
|
|
|
* @param out the output connection that the motor is connected to
|
|
|
|
*/
|
2017-08-08 22:02:11 +02:00
|
|
|
//% blockId=output_start block="turn motor %out|on"
|
2017-08-08 02:39:37 +02:00
|
|
|
//% weight=95 group="Motors"
|
2017-07-07 16:15:36 +02:00
|
|
|
export function start(out: Output) {
|
2017-08-09 19:02:58 +02:00
|
|
|
if (currentSpeed[out] == -1) setSpeed(out, 50)
|
2017-07-11 16:18:59 +02:00
|
|
|
let b = mkCmd(out, DAL.opOutputStart, 0)
|
2017-07-07 16:15:36 +02:00
|
|
|
writePWM(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function reset(out: Output) {
|
2017-07-11 16:18:59 +02:00
|
|
|
let b = mkCmd(out, DAL.opOutputReset, 0)
|
2017-07-07 16:15:36 +02:00
|
|
|
writePWM(b)
|
|
|
|
}
|
|
|
|
|
2017-08-08 11:41:47 +02:00
|
|
|
export function clearCount(out: Output) {
|
|
|
|
let b = mkCmd(out, DAL.opOutputClearCount, 0)
|
|
|
|
writePWM(b)
|
|
|
|
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
|
|
|
if (out & (1 << i)) {
|
|
|
|
motorMM.setNumber(NumberFormat.Int32LE, i * MotorDataOff.Size + MotorDataOff.TachoSensor, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function outOffset(out: Output) {
|
|
|
|
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
|
|
|
if (out & (1 << i))
|
|
|
|
return i * MotorDataOff.Size
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface MotorData {
|
|
|
|
actualSpeed: number; // -100..+100
|
|
|
|
tachoCount: number;
|
|
|
|
count: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only a single output at a time
|
|
|
|
export 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-08-08 22:02:11 +02:00
|
|
|
/**
|
|
|
|
* Get motor speed.
|
|
|
|
* @param out the output connection that the motor is connected to
|
|
|
|
*/
|
|
|
|
//% blockId=output_getCurrentSpeed block="motor %out|speed"
|
|
|
|
//% weight=70 group="Motors"
|
|
|
|
export function getCurrentSpeed(out: Output) {
|
|
|
|
return getMotorData(out).actualSpeed;
|
|
|
|
}
|
|
|
|
|
2017-08-08 02:39:37 +02:00
|
|
|
/**
|
|
|
|
* Set motor speed.
|
|
|
|
* @param out the output connection that the motor is connected to
|
|
|
|
* @param speed the desired speed to use. eg: 100
|
|
|
|
*/
|
|
|
|
//% blockId=output_setSpeed block="set motor %out| speed to %speed"
|
|
|
|
//% weight=81 group="Motors"
|
|
|
|
//% speed.min=-100 speed.max=100
|
2017-07-07 16:15:36 +02:00
|
|
|
export function setSpeed(out: Output, speed: number) {
|
2017-08-09 19:02:58 +02:00
|
|
|
currentSpeed[out] = speed;
|
2017-07-11 16:18:59 +02:00
|
|
|
let b = mkCmd(out, DAL.opOutputSpeed, 1)
|
2017-07-07 16:15:36 +02:00
|
|
|
b.setNumber(NumberFormat.Int8LE, 2, Math.clamp(-100, 100, speed))
|
|
|
|
writePWM(b)
|
|
|
|
}
|
|
|
|
|
2017-08-08 02:39:37 +02:00
|
|
|
/**
|
|
|
|
* Set motor power.
|
|
|
|
* @param out the output connection that the motor is connected to
|
|
|
|
* @param power the desired power to use. eg: 100
|
|
|
|
*/
|
|
|
|
//% blockId=output_setPower block="set motor %out| power to %power"
|
|
|
|
//% weight=80 group="Motors"
|
|
|
|
//% power.min=-100 power.max=100
|
2017-07-07 16:15:36 +02:00
|
|
|
export function setPower(out: Output, power: number) {
|
2017-07-11 16:18:59 +02:00
|
|
|
let b = mkCmd(out, DAL.opOutputPower, 1)
|
2017-07-07 16:15:36 +02:00
|
|
|
b.setNumber(NumberFormat.Int8LE, 2, Math.clamp(-100, 100, power))
|
|
|
|
writePWM(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setPolarity(out: Output, polarity: number) {
|
2017-07-11 16:18:59 +02:00
|
|
|
let b = mkCmd(out, DAL.opOutputPolarity, 1)
|
2017-07-07 16:15:36 +02:00
|
|
|
b.setNumber(NumberFormat.Int8LE, 2, Math.clamp(-1, 1, polarity))
|
|
|
|
writePWM(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface StepOptions {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
export function step(out: Output, opts: StepOptions) {
|
2017-07-11 16:18:59 +02:00
|
|
|
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
|
2017-07-11 16:18:59 +02:00
|
|
|
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) {
|
2017-07-11 16:18:59 +02:00
|
|
|
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
|
|
|
}
|