You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求可单独修改LPT端口特定位的类Out32程序以实现步进电机独立调速

步进电机独立转速控制解决方案

核心思路

要实现单台电机独立调速,需解决两个关键问题:

  • 替代Out32()的全字节写入,实现LPT端口特定位单独修改
  • 为每台电机分配独立线程,用互斥锁避免多线程并发操作端口的冲突

实现位操作的端口工具函数

先补充端口读取函数,再封装单比特位更新逻辑,确保每次只修改目标位,保留其他位状态:

// 新增端口读取函数,与Out32配套
function Inp32(PortAdr: word): byte; stdcall; external 'inpout32.dll';

// 更新指定端口的特定位:BitPos为位索引(0-7,对应LPT的2-9位),Value为0或1
procedure UpdatePortBit(PortAdr: word; BitPos: Integer; Value: Byte);
var
  CurrentData: Byte;
begin
  CurrentData := Inp32(PortAdr);
  if Value = 1 then
    CurrentData := CurrentData or (1 shl BitPos)  // 设置该位为1
  else
    CurrentData := CurrentData and not (1 shl BitPos);  // 设置该位为0
  Out32(PortAdr, CurrentData);
end;

重构线程与控制逻辑

全局变量调整

将单值Vel改为数组,对应每台电机的独立转速:

var
  Vel: array[1..4] of Double;  // 正为顺时针,负为逆时针,0为停止
  PortMutex: THandle;  // 端口操作互斥锁,确保原子性

初始化函数修改

创建互斥锁并为每台电机启动独立控制线程:

procedure InitializeHardware;
var
  id1, id2, id3, id4: LongWord;
  thread1, thread2, thread3, thread4: Integer;
begin
  IsSoftwareControlEngine := true;

  RotateStep[1] := 0;
  RotateStep[2] := 0;
  RotateStep[3] := 0;
  RotateStep[4] := 0;
  RotDir[1] := 0;
  RotDir[2] := 0;
  RotDir[3] := 0;
  RotDir[4] := 0;

  // 创建互斥锁,防止多线程端口操作冲突
  PortMutex := CreateMutex(nil, False, nil);

  // 为每台电机启动独立控制线程
  thread1 := BeginThread(nil, 0, Addr(MotorRotateThread), Pointer(1), 0, id1);
  thread2 := BeginThread(nil, 0, Addr(MotorRotateThread), Pointer(2), 0, id2);
  thread3 := BeginThread(nil, 0, Addr(MotorRotateThread), Pointer(3), 0, id3);
  thread4 := BeginThread(nil, 0, Addr(MotorRotateThread), Pointer(4), 0, id4);

  CloseHandle(thread1);
  CloseHandle(thread2);
  CloseHandle(thread3);
  CloseHandle(thread4);
end;

单电机独立控制线程

每个线程负责对应电机的方向更新与步进脉冲输出,使用独立延迟时间:

function MotorRotateThread(Parameter: pointer): Integer;
const
  // 映射每台电机的方向位、步进位索引(对应LPT端口比特位)
  DirBitMap: array[1..4] of Integer = (1, 3, 5, 7);
  StepBitMap: array[1..4] of Integer = (0, 2, 4, 6);
var
  MotorID: Integer;
  PortAdr: word;
  time_out: double;
  K: Double;  // 乘数因子,根据电机参数调整
begin
  MotorID := Integer(Parameter);
  PortAdr := $EEFC;
  K := 500000;  // 示例值,需根据电机步进角、目标转速校准
  Result := 0;

  while true do
  begin
    if Vel[MotorID] = 0 then
    begin
      // 电机停止:置步进位为0,降低轮询频率减少资源占用
      WaitForSingleObject(PortMutex, INFINITE);
      UpdatePortBit(PortAdr, StepBitMap[MotorID], 0);
      ReleaseMutex(PortMutex);
      Sleep(10);
      Continue;
    end;

    // 计算当前电机的步进延迟
    time_out := K / Abs(Vel[MotorID]);

    // 更新电机方向位
    WaitForSingleObject(PortMutex, INFINITE);
    UpdatePortBit(PortAdr, DirBitMap[MotorID], Ord(Vel[MotorID] < 0));
    ReleaseMutex(PortMutex);

    // 输出步进脉冲:置1→延迟→置0→延迟
    WaitForSingleObject(PortMutex, INFINITE);
    UpdatePortBit(PortAdr, StepBitMap[MotorID], 1);
    ReleaseMutex(PortMutex);
    Delay_mcs(time_out);

    WaitForSingleObject(PortMutex, INFINITE);
    UpdatePortBit(PortAdr, StepBitMap[MotorID], 0);
    ReleaseMutex(PortMutex);
    Delay_mcs(time_out);
  end;

  EndThread(0);
end;

转速更新函数适配

调整ChangeRotateSD适配数组形式的Vel变量:

procedure ChangeRotateSD; // 当SetVel值改变时调用
var
  i: Integer;
begin
  i := AxisNum;
  if Vel[i] > 0 then
  begin
    RotDir[i] := 0;
    RotateStep[i] := 1
  end
  else if Vel[i] < 0 then
  begin
    RotDir[i] := 1;
    RotateStep[i] := 1
  end
  else
  begin
    RotDir[i] := 0;
    RotateStep[i] := 0
  end;
end;

关键注意事项

  • 互斥锁必须严格包裹所有端口操作:任何读、改、写端口的逻辑都要在WaitForSingleObject和ReleaseMutex之间执行,避免并发冲突。
  • 校准乘数因子K:根据电机步进角、最大转速需求计算K值,确保time_out对应的脉冲频率在电机允许范围内。
  • 资源清理:程序退出时调用CloseHandle(PortMutex)释放互斥锁资源。

内容的提问来源于stack exchange,提问作者Расул Барлыбаев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 13:17:47