0

I created 4 threads for each of the 4 stepper motors separately, where an infinite loop will spin with the given Dir and Step and with delays depending on the current speed for each motor. Each call to Out32 will be in a critical section.

In procedure TLPTEngine.ApplyHardware, RotDir[i], RotateStep[i] and speed (delay) TimeOut[i] will be set for each engine. The code below should start spinning the motors when the program starts, but it doesn't. When calling the OutToLptThread[i].Execute procedure in the TLPTEngine.InitializeHardware procedure, an error occurs:

"Exception EAccessViolation in module MyProgram.exe at 001CE279. Access violation at address 005CE279 in module MyProgram.exe'. Read of address 00000000."

and the program is terminated.

uses
SysUtils, Math, TraceLog, MDriveEngines, SyncObjs;

const
  numOfEngines = 4;

type
  // Class of threads that control motors
  TOutToLptThread = class(TThread)
  private
  { Private declarations }
  protected
    procedure Execute; override;
  public
    LPTEngineNum: integer; // Number of engine and thread
  end;

// Class of stepper motors
TLPTEngine = class(TEngine)
public
  procedure InitializeHardware; override;
  procedure FinalizeHardware; override;
  procedure ApplyHardware; override;
end;

var
  RotateStep: array [1 .. numOfEngines] of Integer; // Stepper motor rotation pulse via LPT-port
  RotDir: array [1 .. numOfEngines] of Integer; // Direction of rotation of stepper motor via LPT-port
  Time_out: array [1 .. numOfEngines] of Integer; // Delays in the supply of pulses to the stepper motor through the LPT-port - for different motor speeds

FSynchronizer: TCriticalSection;
OutToLptThread: array[1..numOfEngines] of TOutToLptThread; // Array of threads that control numOfEngines engines
OutToLPTData: Byte; // Common byte with all Dir and Step for tumOfEngines engines, issued to the LPT port

procedure TOutToLptThread.Execute;

procedure Delay_mcs(time_out: double); // Delay in microseconds
var
  iCounterPerSec: TLargeInteger;
  T1, T2: TLargeInteger; // Values of counter before and after operation
begin
  QueryPerformanceFrequency(iCounterPerSec);
  // We defines a frequency of counter
  QueryPerformanceCounter(T1); // Recorded the start time of the operation
  T2 := T1;

  while (T2 - T1) * 1000000 / iCounterPerSec < time_out do
    QueryPerformanceCounter(T2); // Recorded the end time of the operation
  end;

var
  PortAdr: word;
begin
  PortAdr := $EEFC; // Address of LPT-port

  while true do
  begin
    OutToLPTData := RotDir[1] * 2 + RotDir[2] * 8 + RotDir[3] * 32 + RotDir[4] * 128; // Data that output to LPT-port
    FSynchronizer.Enter; // = Acquire;
    try
      Out32(PortAdr, OutToLPTData);
    finally
      FSynchronizer.Leave; // = Release
    end;
    Delay_mcs(Time_out[LPTEngineNum]);
    OutToLPTData := RotateStep[1] + RotateStep[2] * 4 + RotateStep[3] * 16 +
    RotateStep[4] * 64 +
    RotDir[1] * 2 + RotDir[2] * 8 + RotDir[3] * 32 +
    RotDir[4] * 128; // Data that output to LPT-port
    FSynchronizer.Enter; // = Acquire;
    try
      Out32(PortAdr, OutToLPTData);
    finally
      FSynchronizer.Leave; // = Release
    end;
    Delay_mcs(Time_out[LPTEngineNum]);
  end;
end;

procedure TLPTEngine.InitializeHardware; // Called at the start of program execution
var
  i: integer;
begin
  IsSoftwareControlEngine := true;

  for i := 1 to numOfEngines do
  begin
    RotateStep[i] := 1; // Initial pulses of rotation of the stepper motor
    RotDir[i] := 0; // Initial direction of rotation of motors
    Time_out[i] := 500; // Delay in microseconds for each stepper motor
    OutToLptThread[i] := TOutToLptThread.Create(False);
    OutToLptThread[i].LPTEngineNum := i; // The thread number corresponds to the engine number, used in the assignment Time_out[LPTEngineNum]
    OutToLptThread[i].Priority := tpNormal;
//  OutToLptThread[i].Execute; // This command causes a message "Exception EAccessViolation in module MyProgram.exe at 001CE279. Access violation at address 005CE279 in module MyProgram.exe'. Read of address 00000000."
  end;

  inherited;
end;

procedure TLPTEngine.ApplyHardware; // Called when the value of Velocity changes.
var
  i: Integer;
begin
  i := AxisNum; // Number of engine
  if Velocity > 0 then // Direction of Rotation
  begin
    RotDir[i] := 0;
    RotateStep[i] := 1
  end
  else if Velocity < 0 then // Another direction of Rotation
  begin
    RotDir[i] := 1;
    RotateStep[i] := 1
  end
  else
  begin
    RotDir[i] := 0;
    RotateStep[i] := 0
  end;
end;
Eirik A.
  • 447
  • 1
  • 5
  • 11
  • 1
    What's the question? Apart from the fact that no computer has real parallel ports any more but USB devices (a serial interface) that act as parallel ports and the OS doesn't allow direct access to the hardware device anyway? – Panagiotis Kanavos Jun 30 '23 at 18:30
  • 2
    When you have Access Violation with address of 00000000 this usually means you are accessing non-existing object. With quick glance at your code I see you are using global variable `FSynchronizer` which is of type `TCriticalSection;`. [TCriticalSection](https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.SyncObjs.TCriticalSection) is classed object which means you need to create it first before you can use it. Yo do this by [TCriticalSection.Create](https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.SyncObjs.TCriticalSection). – SilverWarior Jun 30 '23 at 18:51
  • 7
    Never call `TThread.Execute()` directly, it is called for you when the thread begins running in the background. You are creating each thread with `CreateSuspended=False`, so it will start running as soon as it is created. Do note that you are initializing `LPTEngineNum` and `Priority` after calling `TOutToLptThread.Create()`, so its thread can start running before those values are set. You should override `Create()` to set the values (the thread doesn't start until `Create` exits), or else create each thread with `CreateSuspended=True` and then call `Start()` after setting the values. – Remy Lebeau Jun 30 '23 at 20:01

0 Answers0