AnasSarkiz/usb-c-pd-brushed-motor
USB-C PD-powered motor controller with protected input, selectable 5/9/12 V buck supply, MCU qualification, PWM H-bridge drive, overvoltage sensing, regenerative energy-dump resistors, and safety interlocks.
- Version
- 0.0.1-a48-cloud-pipeline9-unrouted-c41d79a-305b1e7a
- License
- unset
- Stars
- 0
scripts/electrical-calculations.ts
export interface PdCapability {
voltageV: number
currentA: number
}
// A7 host policy: prefer the lowest adequate fixed-voltage contract.
// Conservative engineering screen; hardware efficiency/thermal measurements pending.
export const inputBudgetReserveFraction = 0.05
export function chooseMotorContract(
sourceCapabilities: PdCapability[],
selection: { motorVoltageV: 5 | 9 | 12; peakMotorCurrentA?: number },
) {
const { motorVoltageV, peakMotorCurrentA = 2.423 } = selection
// 1% initial tolerance plus 100 ppm/K over 100 K, added conservatively.
const minimumLimitA = ((18_000 / 6_980) * 0.9) / 1.02
const maximumLimitA = ((18_000 / 6_980) * 1.1) / 0.98
const adequate = sourceCapabilities
.filter(
(source) =>
(source.voltageV === 15 || source.voltageV === 20) &&
source.currentA >= 3 &&
source.currentA <= 5 &&
source.currentA >= maximumLimitA + 1 / (source.voltageV * 0.95) &&
motorInputCurrentA({
motorVoltageV,
motorCurrentA: peakMotorCurrentA,
contractVoltageV: source.voltageV,
}) *
(1 + inputBudgetReserveFraction) <=
minimumLimitA,
)
.sort((a, b) => a.voltageV - b.voltageV)[0]
return adequate ? { voltageV: adequate.voltageV, currentA: 3 } : null
}
export function motorInputCurrentA({
motorVoltageV,
motorCurrentA,
contractVoltageV,
}: {
motorVoltageV: number
motorCurrentA: number
contractVoltageV: number
}) {
const minimumSourceVoltageV = contractVoltageV * 0.95
const assumedDiodeDropV = 0.55
const assumedBridgeResistanceOhms = 0.36
const assumedBuckEfficiency = 0.85
const assumedAuxiliaryPowerW = 1
const maximumMotorRailV = motorVoltageV * 1.05
const bleederPowerW = maximumMotorRailV ** 2 / 940
const bridgeLossW = motorCurrentA ** 2 * assumedBridgeResistanceOhms
return (
(maximumMotorRailV * motorCurrentA + bridgeLossW + bleederPowerW) /
assumedBuckEfficiency /
(minimumSourceVoltageV - assumedDiodeDropV) +
assumedAuxiliaryPowerW / minimumSourceVoltageV
)
}
export function regulatedMotorVoltageV(parallelBranchOhms?: number) {
const feedbackTopOhms = 100_000 + 5_100
const feedbackBottomOhms = parallelBranchOhms
? 1 / (1 / 20_000 + 1 / parallelBranchOhms)
: 20_000
return 0.8 * (1 + feedbackTopOhms / feedbackBottomOhms)
}
export function pwmTiming({
fraction,
potResistanceOhms = 10_000,
timingCapacitanceF = 5.6e-9,
}: {
fraction: number
potResistanceOhms?: number
timingCapacitanceF?: number
}) {
// Analytic estimate including diode and discharge transistor drops.
// These nominal drops are assumptions; no simulated/bench guarantee.
const supplyV = 3.3
const diodeDropV = 0.2
const dischargeDropV = 0.1
const lowerThresholdV = supplyV / 3
const upperThresholdV = (supplyV * 2) / 3
const chargeTimeS =
timingCapacitanceF *
(330 + fraction * potResistanceOhms) *
Math.log(
(supplyV - diodeDropV - lowerThresholdV) /
(supplyV - diodeDropV - upperThresholdV),
)
const dischargeTimeS =
timingCapacitanceF *
(330 + (1 - fraction) * potResistanceOhms) *
Math.log(
(upperThresholdV - diodeDropV - dischargeDropV) /
(lowerThresholdV - diodeDropV - dischargeDropV),
)
return {
frequencyHz: 1 / (chargeTimeS + dischargeTimeS),
dutyFraction: chargeTimeS / (chargeTimeS + dischargeTimeS),
}
}