S7 OB40 Hardware Interrupt: Encoder Zero Pulse Detection

David Krause14 min read
S7-300SiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview

Detecting a single zero-mark pulse from a rotating shaft encoder is a classic S7 application: each mechanical revolution produces one short pulse that must be latched and translated into a usable boolean flag or a one-shot output for downstream logic such as a stepper-motor homing sequence, a cut-to-length counter, or a synchronization trigger.

At low shaft speeds, a hardware interrupt organization block (OB 40) on a digital input gives the cleanest response because the interrupt is serviced in microseconds, independent of the OB1 cycle time. The original application described in the field report was an encoder running at 150 RPM wired to %I0.3 on an S7-300 CPU, processed in OB 40. This article documents the complete configuration, the correct use of edge detection, the timing budget at 150 RPM, the recommended software pattern, and the alternative path using a high-speed counter (HSC) when the requirements scale up.

Siemens documents hardware-interrupt OBs in the official S7-1500 functional description and the S7-300/S7-400 organization-block reference. Refer to the Hardware interrupt OBs (S7-1500) manual and the Hardware interrupt OBs OB 40 to OB 47 (S7-300/S7-400) reference for the platform-specific event semantics.

Prerequisites

Before configuring OB 40, verify the following prerequisites so the interrupt actually fires and the captured event is trusted.

  1. CPU and signal module capability. The DI module channel used (here %I0.3) must support hardware interrupts. On S7-300, the SM 321 digital input modules in the 6ES7321- family with hardware-interrupt capability (for example, 6ES7321-7BH01-0AB0) must be used. On S7-1500, the onboard DIs of the CPU and most SM 521 DI modules (6ES7521- series) support hardware interrupts natively.
  2. Encoder pulse width and input filter. A zero-mark pulse from a typical incremental encoder at 150 RPM is at least 90° electrical (a quarter revolution) wide. At 150 RPM the period is 400 ms, so the zero pulse is at least ~100 ms long, well above any 20 µs input filter on the DI module. The input filter must be set short enough not to swallow the pulse.
  3. Wiring. Encoder signal common tied to the M terminal of the DI module. For PNP encoders, use a source-type wiring (24 V on the input, encoder sinks). Shielded twisted pair recommended with shield grounded at the cabinet entry only.
  4. STEP 7 / TIA Portal project. The hardware configuration must be compiled and downloaded before OB 40 events are enabled. Without a compiled HWConfig, the digital input still toggles, but the interrupt will not be armed.
  5. Cycle-time budget. 150 RPM = 2.5 revolutions per second → period T = 1 / 2.5 = 0.4 s = 400 ms. The OB1 cycle time must be less than 400 ms, otherwise a flag set in OB 40 could be cleared before the next zero mark arrives and the cyclic program would never observe it. Typical S7-300 OB1 times are 5–50 ms, so this is rarely a problem, but it must be checked on the deployed CPU.

Understanding Hardware Interrupt OBs

A hardware interrupt OB is a fixed-priority routine that the CPU enters on a configured digital-event edge, regardless of where OB 1 is in its scan. On S7-300 / S7-400 the eight hardware interrupt OBs are OB 40 through OB 47; on S7-1500 the same range is available and is augmented by event-driven OBs. If only one hardware interrupt source is configured, all events route to OB 40 by default.

Per the Siemens reference, OB 40 has local temp data (L-stack) you can read to determine which channel triggered the event and at what timestamp:

  • OB40_MDL_ADDR (WORD) – base address of the module that triggered the interrupt.
  • OB40_POINT_ADDR (DWORD) – bit pattern showing which channel(s) fired.
  • OB40_TIMESTAMP / OB40_DATE_TIME – time of the event (S7-1500 / 400 provide finer resolution than S7-300).
  • OB40_FLT_ID (BYTE) – fault/error identifier, only non-zero on faults.

Reading OB40_POINT_ADDR is a robust pattern because it lets one OB 40 service multiple channels from multiple modules without ambiguity, and it does not rely on the symbol name of the input.

Encoder Zero-Pulse Timing Analysis at 150 RPM

The timing budget is the key design constraint. The parameters in the field report give a concrete envelope:

Parameter Symbol Value at 150 RPM Comment
Shaft speed n 150 min-1 2.5 rev/s
Rev period Trev 400 ms 1 / (n / 60)
Zero-pulse width (typ., 90° elec.) tzp ~100 ms Trev / 4
Min OB1 cycle to see the flag tcyc < 400 ms flag set in OB 40 must not be cleared before next OB1 sees it
Hardware-interrupt latency tint < 1 ms S7-300 typical; S7-1500 sub-100 µs
Debounce requirement tdb 0 ms (encoder-clean) Edge in HW config already debounces via input filter

If the OB1 cycle is not guaranteed below 400 ms, the simple "set in OB 40, reset in OB 1" pattern will fail intermittently. The fix is either (a) shorten the cycle, (b) reset the flag in OB 40 itself after a short timer, or (c) move to HSC counting where the zero mark becomes one count in hardware.

Step-by-Step: Configuring OB 40 in STEP 7 / TIA Portal

Step 1 - Enable hardware interrupts on the digital input

In the device configuration of the DI module (or the onboard DI of an S7-1500 CPU), open the properties of the channel I0.3 (or the equivalent byte/bit used):

  1. Set "Enable hardware interrupt" = yes.
  2. Set Trigger for hardware interrupt = rising edge (recommended for a positive-going zero mark) or falling edge if the encoder idles low and pulses high.
  3. Set the input filter to a value shorter than the zero-pulse width. With a 100 ms pulse at 150 RPM, any filter ≤ 10 ms is safe.
  4. Assign the OB to OB 40 in the "Hardware interrupt assignment" dropdown. Only one OB 40–47 is invoked per event, and the default is OB 40.
If the digital input module is a basic SM 321 (e.g. 6ES7321-1BH02-0AA0) that does not list hardware interrupts in its properties, OB 40 will never fire. Replace the module with an interrupt-capable variant (e.g. 6ES7321-7BH01-0AB0) or move to HSC.

Step 2 - Create OB 40 in the project

In the program blocks tree, add a new organization block, name it OB 40, and select the type Hardware interrupt. The editor opens with the standard temp interface. Leave the priority at the default 16 for S7-300 / S7-400; S7-1500 typically uses priority 18 for hardware interrupts.

Step 3 - Write the OB 40 body

The original poster asked how to derive a clean per-revolution pulse from a hardware interrupt. The recommended pattern, which several forum responders confirmed, is to set a flag in OB 40 and reset it in OB 1 after OB 1 has read it. This converts the asynchronous, sub-millisecond interrupt into a deterministic boolean the cyclic program can sample.

LAD (FBD) – set flag in OB 40

// Network 1: edge detection on the interrupt source
A     "ZeroMark_Input"          // %I0.3 (only for clarity - interrupt already filtered)
FP    "ZeroMark_Edge"           // edge bit in static DB or M-bit

// Network 2: set the per-revolution flag and update a counter
A     "ZeroMark_Edge"
S     "EncZero_Flag"            // sticky flag read by OB1

// Network 3: optional counter
A     "EncZero_Flag"
CU    "EncZero_Counter"         // CTU instance, increments each rev

// Network 4: optional per-revolution one-shot for a stepper start pulse
A     "EncZero_Flag"
=     "EncZero_OneShot"         // held true; reset by OB1 after the start trigger acknowledges

ST – read event metadata in OB 40 (more robust for multiple sources)

// OB 40 - Hardware Interrupt
// Triggered by rising edge on %I0.3 (encoder zero mark)
#rawPoint   := OB40_POINT_ADDR;    // DWORD - bit-mask of firing channel(s)
#modAddr    := OB40_MDL_ADDR;      // WORD  - logical base address of module

// Did %I0.3 fire? bit 3 of the channel byte
IF (#rawPoint AND DWORD#16#00000008) <> DWORD#16#0 THEN
    "EncZero_Flag"        := TRUE;
    "EncZero_Counter".CV  := "EncZero_Counter".CV + 1;
    "EncZero_Timestamp"   := OB40_TIMESTAMP;   // 100-ns ticks on S7-1500
END_IF;

Inside OB 1 the matching reset block is needed:

// OB 1 - Cyclic, typical scan 5-50 ms
// Consume the per-revolution flag and clear it
A     "EncZero_Flag"
JCN   _skip

S     "Stepper_HomeTrigger"     // fire downstream start command
CU    "RevCounter_OB1"          // diagnostic counter
R     "EncZero_Flag"            // clear for the next rev

_skip: NOP 0

Step 4 - Verify the assignment in HW config

In STEP 7 (classic), the assignment of a hardware-interrupt channel to OB 40 is in HW Config → DI module → Properties → Inputs → "Hardware interrupt" tab. In TIA Portal, it is in Device configuration → Module → Channel → "Hardware interrupt". Without a valid assignment, the editor warns "Hardware interrupt for this channel is not assigned to any OB" and OB 40 is never entered.

Edge Detection: Why "FP" Alone Is Not the Problem

A common misconception in OB 40 is that an internal FP (rising-edge) bit in the cyclic program would be required to detect the zero mark. The opposite is true: the hardware already generated the edge and called OB 40 only because of that edge. Adding FP inside OB 40 is harmless and can be useful if OB 40 is wired to multiple channels via the same module. The hardware arming in the DI module properties is what matters; OB 40 itself can simply set a flag.

High-Speed Counter (HSC) Alternative

When the encoder delivers both A/B quadrature and a zero mark, or when the cycle becomes the bottleneck, configure the HSC rather than a discrete input. The HSC counts in hardware, captures the zero mark automatically, and stores the result in the process image of the technology object.

Aspect Hardware interrupt OB 40 High-Speed counter (HSC)
Typical use Single digital event per rev (zero mark, proximity switch) A/B quadrature, pulse counting, position capture
Speed limit Limited by DI filter and CPU interrupt latency (typ. ≤ 1 kHz) Hardware counters on S7-1500: up to 1 MHz, S7-300: 30 kHz–200 kHz depending on module
Zero-mark handling OB 40 body sets/clears a flag manually Zero mark is captured as a hardware latched position value
Configuration effort One channel property + one OB 40 Technology object, HSC configuration, wiring rules
CPU load Negligible for 2.5 Hz Negligible (hardware path)
Recommended for 150 RPM zero-only application Yes – simplest Overkill, but useful if A/B is also wired

For a zero-pulse-only application at 150 RPM, OB 40 is the leanest solution. HSC is the right choice the moment the application needs the absolute count between zero marks, a direction signal, or a captured position at the zero event.

Verification and Diagnostics

  1. Online → Monitoring OB 40. Force a manual edge on the DI with a wire or a function generator. In TIA Portal, go online and watch the OB 40 call environment; OB40_POINT_ADDR must show the bit mask of the firing channel.
  2. Watch the flag from OB 1. Add the "EncZero_Flag" tag to a watch table and verify it transitions from FALSE to TRUE on each manual edge and returns to FALSE on the next OB 1 scan.
  3. Count revolutions. Add a counter tag (e.g. CTU instance) incremented in OB 40. With a hand-cranked or motor-driven encoder at known RPM, the counter should match real time × RPM / 60.
  4. Cycle-time check. Read the OB1 cycle time on the CPU diagnostic buffer (S7-300) or in the online diagnostics of the S7-1500. Confirm it is well under 400 ms. If it is not, the flag could be cleared between revolutions and the count will stall.
  5. Diagnostic buffer on the CPU. If OB 40 never fires, open the diagnostic buffer and look for "Hardware interrupt lost" events – this means the event rate exceeded what the OB could service.

Troubleshooting Matrix

Symptom Probable cause Action
OB 40 never fires DI module is not hardware-interrupt capable, or "Enable hardware interrupt" is off, or OB is not assigned in the module properties Replace module with a 6ES7321-7BH01-0AB0 (S7-300) or a 6ES7521- SM (S7-1500); enable the channel; assign OB 40
OB 40 fires continuously Input filter is shorter than the contact-bounce of the wiring, or the encoder is chattering Increase input filter to 1–5 ms; check the encoder power supply for noise; add a small RC snubber on the input
Flag set in OB 40, but never seen in OB 1 OB1 cycle exceeds the time between two zero marks, or the flag is reset too early Measure OB1 cycle; reset the flag in OB 40 itself with a delayed reset; or move to HSC
Count drifts after long runs Mechanical slip or extra zero marks from electrical noise Check encoder coupling; add shielded cable; verify the input filter; consider using HSC with hardware filtering
Stepper-motor start pulse missing on first rev OB 1 is in the middle of its scan when OB 40 fires, and the trigger is processed in the next cycle This is normal. If the consumer must react in < 1 OB1 cycle, drive the consumer from inside OB 40 directly (or from a synchronous OB)
CPU goes into STOP with OB 40 priority/class error OB 40 is configured but contains an error; S7-300 stops by default Add an error-handling OB (OB 80–87) and a stub in OB 40; check diagnostic buffer for the stop cause

S7-1500 Specific Notes

On S7-1500 CPUs, hardware interrupts are configured identically but with a richer event model. The relevant references are the S7-1500 hardware interrupt OB manual page and the device-configuration properties of the DI channel. Notable differences versus S7-300:

  • The CPU enters OB 40 in well under 100 µs, even with a large OB1 program. The 400 ms cycle budget is not a concern.
  • Hardware interrupts are organized into interrupt groups, and each group can be assigned to a specific OB. By default all groups still go to OB 40.
  • OB40_TIMESTAMP returns 100-ns ticks, useful for diagnostic speed measurement (Δt between zero marks gives instantaneous RPM).
  • If the application has multiple event sources per OB 40, the cleanest pattern is to read OB40_POINT_ADDR and switch on the firing bit, exactly as shown in the ST snippet above.

Sample ST Block: Per-Revolution One-Shot

The original request was for a single clean pulse each revolution. A robust S7-1500 ST implementation in OB 40:

// OB 40 - per-revolution event
#eventBits  := OB40_POINT_ADDR;        // DWORD
#moduleBase := OB40_MDL_ADDR;          // WORD
#stamp      := OB40_TIMESTAMP;         // 100-ns ticks

// Fire on %I0.3 (bit 3 of byte 0)
IF (#eventBits AND DWORD#16#00000008) <> 0 THEN
    // 1) toggle a sticky flag for OB1
    "EncZero_Flag" := TRUE;

    // 2) increment a hardware-synchronized counter
    "RevCount".CV  := "RevCount".CV + 1;

    // 3) capture a high-resolution timestamp
    "EncZero_TimePrev"  := "EncZero_Time";
    "EncZero_Time"      := #stamp;

    // 4) drive the one-shot pulse directly from OB40 to avoid OB1 latency
    "Stepper_HomeTrigger" := TRUE;
END_IF;

The corresponding OB 1 logic then releases the trigger when the consumer acknowledges it. The trigger is short and deterministic; no part of the chain depends on the OB1 scan period.

Field-Proven Caveats

  • OB 40 priority is 16 on S7-300/400. Any OB with a higher priority (e.g. OB 82 diagnostic) will pre-empt it. If the zero mark is critical, do not call FC/FB that itself uses higher-priority OBs.
  • Long-running FCs called from OB 40 will delay OB 1. Keep OB 40 lean: set a flag, increment a counter, capture a timestamp, and return.
  • Do not call WR_PARM, WR_REC, or any SFB/SFC that uses internal communication in OB 40 without verifying it is allowed; some SFCs require OB 1 priority.
  • The diagnostic buffer of the CPU records every OB 40 entry when "OB 40 call counter" is enabled; for long-running installations, consider disabling this to keep the buffer small.

Does OB 40 fire on both edges of the zero pulse?

Only the edge selected in the DI module channel properties (rising, falling, or both). For a single event per revolution, configure "rising edge" and make sure the pulse has a clean 24 V level. Configure a hardware filter ≤ 10 ms to suppress contact bounce.

What is the maximum zero-pulse frequency OB 40 can handle?

It depends on the OB 40 execution time and the CPU. With a few microseconds of code, an S7-300 can sustain tens of kHz; an S7-1500 can sustain hundreds of kHz before "Hardware interrupt lost" events appear in the diagnostic buffer. At 150 RPM (2.5 Hz) the load is negligible.

My flag set in OB 40 is never TRUE in OB 1. Why?

Most often the OB1 cycle time is longer than the time between two zero pulses, so the flag is set and reset within the same OB1 scan. Check the OB1 cycle time, shorten it, or reset the flag in OB 40 instead of OB 1. Another common cause is the wrong memory area – confirm the flag is in a retentive or non-retentive M/DB area accessible from both OBs.

Is OB 40 the same on S7-300 and S7-1500?

The numbering and role are the same (OB 40 is the default hardware interrupt OB on both platforms), but the S7-1500 provides richer event metadata, more interrupt groups, and sub-100 µs latency. See the S7-1500 hardware interrupt OB reference for the exact temp interface and the S7-300/400 OB 40–47 reference for S7-300/400 specifics.

Should I use OB 40 or a high-speed counter for an encoder zero mark?

Use OB 40 when the application only needs a one-shot flag per revolution and the pulse rate is modest (≤ 1 kHz). Use the HSC technology object when you also need the A/B count, direction, period measurement, or when the input rate is high. For 150 RPM with a zero-pulse-only requirement, OB 40 is the leaner choice.

Back to blog