hal_pru_generic - Machinekit HAL driver for the TI Programmable Realtime Unit (PRU).


loadrt hal_pru_generic [argument]…​

Valid arguments are:

  • prucode=<file.bin>

  • pru=[0|1]

  • pru_period=N

  • halname=<Name>

  • num_stepgens=N

  • num_pwmgens=N[,N]

  • num_encoders=N[,N]

  • disabled=[0|1]]

prucode [required]

Binary PRU firmware to load and run on the specified PRU.

pru [optional, default: 1]

Which PRU (0 or 1) to use to run the code. This only matters if you are using direct PRU I/O pins, in which case the PRU specified needs to be able to access the pins used in the HAL configuration.

pru_period [optional, default: 10000]

The task period time for the PRU, in nano-seconds. The default of 10000 nS results in a PRU task frequency of 100 KHz. Smaller values here provide better timing performance for pwm and step/dir generators, but the setting must be large enough to allow all PRU tasks to complete.

halname [optional, default: hal_pru_generic]

This identifier is used to prefix all created HAL pins and parameters. The default is for legacy configurations and should otherwise be avoided.


You MUST set the name to a short one
Recent convention sets halname=hpg to provide for shorter HAL names.

This is very important to do, because additions to HAL pins, mean some pins names will exceed a 49 charactor limit, if you use 15 charactors just for the base name.

num_stepgens [optional, default: 0]

Create N stepgen instances.

num_pwmgens [optional, default: 0]

Create pwmgen instance[s] with N channels.

num_encoders [optional, default: 0]

Create N encoder instances.

disabled [optional, default: 0]

If you set disabled=1, the driver will start with the PRU in the disabled state, which can be useful for debugging. In this state, it is possible to single-step through the PRU startup code after the driver is loaded.


hal_pru_generic is a device driver that uses the PRU subsystem available in Texas Instruments Sitara processors like on the BeagleBone SBC to perform high-speed hard-real-time bit-twiddling to generate step/direction and PWM outputs, avoiding the need to run a base thread on the ARM core which has fairly poor latency.

The driver is written for flexibility rather than raw speed (hence the 'generic' in the name). Various functions are supported by small bits of PRU code (tasklets) that are linked into a function chain for the PRU when the driver is first loaded. The minimum pru_period (and thus the maximum step frequency) is determined by how long the PRU takes to execute all configured tasklets, so simple configurations can run faster than complex setups with lots of outputs.

Currently, there are tasklets written for step/direction generation, PWM output, and encoder inputs (using direct PRU input pins).


The hal_pru_generic driver runs on a BeagleBone and has used a variety of numbering schemes to identify I/O pins. All legacy pin numbering schemes are still supported for backwards compatibility, but are not documented here to avoid confusion (see fixup_pin() the code if you really need to know how the previous schemes worked). The current numbering scheme is based on the P8/P9 pin headers found on the BeagleBone.

Pins are specified using a 4 digit value. The four digit value is composed of three fields:

direction digit - an optional prefix to indicate direct PRU I/O
  • 0 or no prefix = standard GPIO pin

  • 1 = PRU direct output

  • 2 = PRU direct input

header digit
  • 8 or 9 to indicate the header (P8 or P9)

pin number on P8 or P9 header
  • 2 digits to represent the specific pin on the P8 or P9 header


0811 = GPIO on P8, pin 11
 811 = GPIO on P8, pin 11 (identical to above)
1811 = PRU direct output on P8, pin 11
2927 = PRU direct input on P9, pin 27

Note that not all P8/P9 pins can be used as I/O (some pins are dedicated to specific signals or power) and not all GPIO pins can be used as PRU direct input or output.

Direct PRU I/O pins are attatched to a specific PRU. If you choose to use direct I/O pins, you are responsible for selecting a valid combination of pin numbers and pru= such that the PRU code runs on a PRU core that can access the configured pins.

This driver does not setup any pin multiplexing or GPIO settings (pin direction, pull-up/down). The user is required to configure these settings prior to launching the driver, typically by using either a device tree overlay or the cape-universal overlay and the config-pin utility.


stepgens have names like

"Instance" is a two-digit number that corresponds to the stepgen instance number. There are 'num_stepgens' instances, starting with 00.

Each stepgen uses 2 IO pins, one for the step signal and one for the direction signal.

Each stepgen instance has the following pins and parameters:


position-cmd (float input)

Target position of stepper motion, in arbitrary position units. This pin is only used when the stepgen is in position control mode (control-type=0).

velocity-cmd (float input)

Target velocity of stepper motion, in arbitrary position units per second. This pin is only used when the stepgen is in velocity control mode (control-type=1).

position-fb (float output)

Feedback position in arbitrary position units. This is similar to "counts/position_scale", but has finer than step resolution.

velocity-fb (float output)

Feedback velocity in arbitrary position units per second.

counts (s32 output)

Feedback position in counts (number of steps).

enable (bit input)

This pin enables the step generator instance. When True, the stepgen instance works as expected. When False, no steps are generated and velocity-fb goes immediately to 0. If the stepgen is moving when enable goes false it stops immediately, without obeying the maxaccel limit.

control-type (bit input)

Switches between position control mode (0) and velocity control mode (1). Defaults to position control (0).

position-scale (float input)

Converts from counts to position units. position = counts / position_scale

maxvel (float input)

Maximum speed, in position units per second. If set to 0, the driver will always use the maximum possible velocity based on the current step timings and position-scale. The max velocity will change if the step timings or position-scale changes. Defaults to 0.

maxaccel (float input)

Maximum acceleration, in position units per second per second. Defaults to 1.0. If set to 0, the driver will not limit its acceleration at all - this requires that the position-cmd or velocity-cmd pin is driven in a way that does not exceed the machine’s capabilities. This is probably what you want if you’re going to be using the Machinekit trajectory planner to jog or run G-code.

steplen (u32 input)

Minimum duration of the step signal, in nanoseconds.

stepspace (u32 input)

Minimum interval between step signals, in nanoseconds.

dirsetup (u32 input)

Minimum duration of stable Direction signal before a step begins, in nanoseconds.

dirhold (u32 input)

Minimum duration of stable Direction signal after a step ends, in nanoseconds.

step_pin (u32 input)

I/O pin to use for the step output

dir_pin (u32 input)

I/O pin to use for the direction output

stepinvert (bit input)

Inverts the step output (normally high with pulses going low)


pwmgens have names like

"Instance" is a two-digit number that corresponds to the pwmgen instance number. Each channel number value passed to 'num_pwmgens' creates a pwmgen instance, starting with 00. "Channel" is a two-digit number that corresponds to a specific pwmgen channel in a pwmgen instance, starting with 00.

Each pwmgen instance has a single pwm_peroid, so all channels in that instance have the same pwm frequency. To support multiple PWM frequencies, it is possible to create more than one PWM instance, with each instance having a configurable number of channels. To do so, pass multiple values separated by commas to num_pwmgens, eg: num_pwmgens=2,1 would create 2 pwmgen instances with a total of 3 channels:

hpg.pwmgen.00.out.00 hpg.pwmgen.00.out.01 hpg.pwmgen.01.out.00

Each pwmgen uses 1 IO pin


enable (bit input)

If true, the pwmgen will output pulses. If 'enable' is false, pwmgen will not output any signals.

value (float input)

The current pwmgen command value, in arbitrary units.

scale (float input)

Scaling factor to convert 'value' from arbitrary units to duty cycle: dc = value / scale. Duty cycle has an effective range of -1.0 to +1.0 inclusive, anything outside that range gets clipped. The default scale is 1.0.

pin (u32 input)

I/O pin to use for the pwm output

In addition to the per-channel pins listed above, there is one setting that affects all pwmgen channels in an instance:

pwm_period (u32 input)

This specifies the PWM period, in nS. The default is 10,000,000 nS, or 100 Hz. The lower bound is determined by the pru_period and the desired resolution. The longer the pwm_period, the more bits of resolution are available but the lower the PWM frequency.


Encoders have names like

"Instance" is a two-digit number that corresponds to the encoder instance number. Each channel number value passed to 'num_encoders' creates an encoder instance, starting with 00. "Channel" is a two-digit number that corresponds to a specific encoder channel in an encoder instance, starting with 00.

Currently, there is no particular reason to use multiple encoder instances unless you happen to prefer the way the pins are exported to HAL (perhaps to keep encoders for motion separate from encoders for a jog wheel). Support for multiple instances is intended to allow for different settings for high-speed encoder instances using direct PRU input pins and lower-speed instances that access standard GPIO pins.

Currently all encoders MUST use direct PRU input pins.

Each encoder uses up to three PRU direct input pins, depending on the counting mode.

The encoder uses different pin numbering than the rest of the hal_pru_generic driver. The pin number values for (A|B|index)-pin should be the PRU dedicated input pin number, a value in the range of 0-16 inclusive. It is the user’s responsibility to insure the physical I/O pins used are available as direct PRU input pins on the PRU used to run the hal_pru_generic PRU binary. Unused pins should be assigned to a PRU input that will not change value (the 'unconnected' inputs 17-29 work well for this).

Each encoder instance has the following pins:


count (s32 output)

Number of encoder counts since the previous reset.

position (float output)

Encoder position in position units (count / scale).

velocity (float output)

Estimated encoder velocity in position units per second.

reset (bit input)

Currently unsupported, included for compatability with hm2.

index-enable (bit in/out)

When this pin is set to True, the count (and therefore also position) are reset to zero on the next Index (Phase-Z) pulse. At the same time, index-enable is reset to zero to indicate that the pulse has occurred.

rawcount (s32 output)

Total number of encoder counts since the start, not adjusted for index or reset.

scale (float input)

Converts from 'count' units to 'position' units.

A-pin (u32 input)

PRU direct input pin to use for the A signal

A-invert (bit input)

If True, inverts the polarity of the A signal

B-pin (u32 input)

PRU direct input pin to use for the B signal

B-invert (bit input)

If True, inverts the polarity of the B signal

index-pin (u32 input)

PRU direct input pin to use for the index (Z) signal

index-invert (bit input)

If True, the rising edge of the Index input pin triggers the Index event (if index-enable is True). If set to False, the falling edge triggers.

index-mask (bit input)

Currently unsupported, included for compatability with hm2.

index-mask-invert (bit input)

Currently unsupported, included for compatability with hm2.

counter-mode (u32 input)
Value Function


Quadrature mode (default)


Step/Dir mode (A = Step, B = Up/Down)


Up counter (counts rising edges on A, B is ignored)


Quadrature x1 mode (matches HAL software encoder x1 mode)

All other values


filter (bit input)

Currently unsupported, included for compatability with hm2.

vel-timeout (float input)

When the encoder is moving slower than one pulse for each time that the driver reads the count from the PRU (in the capture-position() function), the velocity is harder to estimate. The driver can wait several iterations for the next pulse to arrive, all the while reporting the upper bound of the encoder velocity, which can be accurately guessed. This parameter specifies how long to wait for the next pulse, before reporting the encoder stopped. This parameter is in seconds.



This reads the encoder counters and stepgen feedbacks from the PRU.


This updates the PWM duty cycles and stepgen rates on the PRU. Any changes to configuration pins such as stepgen timing, inversions, etc, are also effected by this function.