Configuration Example: DC541-CM Used as Interrupt I/O Device¶
Hardware configuration
The example control system shall have the following configuration:
- Terminal base TB521 (two Communication Module slots)
- DC541-CM in Communication Module slot 1 (first slot on the left of the CPU)
- PM591-ETH
- I/O module DC532 on the I/O Bus
Wiring
The channels are connected as follows:
DC532 / C16 ————– DC541 / C0
DC532 / C17 ————– DC541 / C1
DC532 / C18 ————– DC541 / C2
DC532 / C19 ————– DC541 / C3
DC532 / C20 ————– DC541 / C4
DC532 / C21 ————– DC541 / C5
PLC configuration
- DC541-CM in slot 1, operating mode “IO mode”
- Configuration: | Channels | C0…C4 | Interrupt input |
Channel | C5 | Input | |
Channels | C6…C7 | Outputs |
- Specification of the ETHERNET Communication Module as internal Communication Module (if available)
- DC532 on the I/O bus
Task configuration
- Task 1: Cyclic program / Prio = 10 / Interval = t#10ms / PLC_PRG
- Task 2: HIGHInterrupt_1 / DC541_Interrupt_Ext1()
Purpose of the interrupt program DC541_Interrupt_Ext1()
The interrupt program should fulfill the following functionality:
- Counting of all interrupts
- Counting of the interrupts per input
- Calculation of the interrupt frequency in [Int/s]
- Reporting of the number of interrupts per input
- Input C4: Resetting the counters
- Input C5: Input
- Output C6: Status of input C5
- Output C7: Toggle output
The declaration part of the program looks as follows:
PROGRAM DC541_Interrupt_Ext1 | |||
VAR | |||
dwIntCount | : DWORD; | (* count all interrupts *) | |
dwIntCountOld | : DWORD; | (* start value for next measure *) | |
tActual | : TIME; | (* systemtick in ms *) | |
tStart | : TIME; | (* start value of systemtick for next calculation *) | |
dwUsedTime | : DWORD; | (* time for 1000 interrupts in ms *) | |
dwFrequenz | : DWORD; | (* interrupt frequency in [Int / sec] *) | |
DC541_IntSource | : DC541_INT_IN; | (* instance FB: read interrupt source *) | |
DC541_Ios | : DC541_IO; | (* instance FB: read/write inputs/outputs *) | |
dwCount_InX | : ARRAY[0..cbyDC541_IntInp] OF DWORD; | (* count interrupts of In0..In3 *) | |
dwCount_InXOld | : ARRAY[0..cbyDC541_IntInp] OF DWORD; | (* start value for next 1000 interrupts *) | |
dwIntHisto | : ARRAY[0..cbyDC541_IntInp, 0..cbyDC541_MaxHist] OF DWORD; | (* histo data C0…C3 *) | |
wIndex | : WORD; | (* index for histo data *) | |
byInd | : BYTE; | (* loop index *) | |
END_VAR | |||
VAR CONSTANT | |||
cbyDC541_SLOT | : BYTE := 1; | (* SLOT number of DC541 *) | |
cbyDC541_MaxHist | : BYTE := 9; | (* max number of histo entries *) | |
cbyDC541_IntInp | : BYTE := 4; | (* number of interrupt inputs -1 *) | |
END_VAR |
The instruction part looks as follows:
At the beginning, the interrupts are counted in dwIntCount. After each 1000 interrupts, a calculation of the frequency is performed and the counting values for the interrupts per input are stored.
dwIntCount := dwIntCount + 1; | (* count all interrupts *) | |
IF dwIntCount - dwIntCountOld >= 1000 THEN | (* after 1000 interrupts -> calculate frequency *) | |
dwIntCountOld := dwIntCount; | (* save dwIntCount for next call *) | |
tActual := TIME(); | ||
dwUsedTime := TIME_TO_DWORD(tActual - tStart); | (* duration in ms for 1000 interrupts *) | |
dwFrequenz := 1000000 / dwUsedTime; | (* [Interrupt / sec] 1000 Int * 1000 ms/sec *) | |
tStart := tActual; | (* for next measure *) | |
dwIntHisto[0,wIndex] := dwCount_InX[0] - dwCount_InXOld[0]; | (* IN0 interrupts of last 1000 *) | |
dwCount_InXOld[0] :=dwCount_InX[0]; | (* start value for next measure *) | |
dwIntHisto[1,wIndex] := dwCount_InX[1] - dwCount_InXOld[1]; | (* IN1 interrupts of last 1000 *) | |
dwCount_InXOld[1] :=dwCount_InX[1]; | (* start value for next measure *) | |
dwIntHisto[2,wIndex] := dwCount_InX[2] - dwCount_InXOld[2]; | (* IN2 interrupts of last 1000 *) | |
dwCount_InXOld[2] :=dwCount_InX[2]; | (* start value for next measure *) | |
dwIntHisto[3,wIndex] := dwCount_InX[3] - dwCount_InXOld[3]; | (* IN3 interrupts of last 1000 *) | |
dwCount_InXOld[3] :=dwCount_InX[3]; | (* start value for next measure *) | |
wIndex := wIndex + 1; | (* increase index *) | |
IF wIndex > cbyDC541_MaxHist THEN wIndex := 0; END_IF; | (* reset index, if >1000 *) | |
END_IF; (* 1000 Interrupts *) |
After this, the block DC541_INT_IN is called to identify the interrupt source and then the interrupt counters of the channels are updated depending on the outputs of this block.
(* Read interrupt source –> if output = TRUE –> interrupt since last call *)
DC541_IntSource(EN := TRUE, SLOT := cbyDC541_SLOT);
(* count the interrupts for each interrupt input C0..C3 *)
dwCount_InX[0] := dwCount_InX[0] + BOOL_TO_DWORD(DC541_IntSource.IN0);
dwCount_InX[1] := dwCount_InX[1] + BOOL_TO_DWORD(DC541_IntSource.IN1);
dwCount_InX[2] := dwCount_InX[2] + BOOL_TO_DWORD(DC541_IntSource.IN2);
dwCount_InX[3] := dwCount_InX[3] + BOOL_TO_DWORD(DC541_IntSource.IN3);
dwCount_InX[4] := dwCount_InX[4] + BOOL_TO_DWORD(DC541_IntSource.IN4);
In case of an interrupt on channel 4, the counters are reset.
IF DC541_IntSource.IN4 THEN | (* Input channel C4 = TRUE *) | |
dwIntCount := dwIntCountOld := 0; | (* reset count all interrupts *) | |
FOR byInd := 0 TO cbyDC541_IntInp-1 DO | (* reset channel interrupt counters C0..C3 *) | |
dwCount_InX[byInd] := dwCount_InXOld[byInd] := 0; | ||
END_FOR; (* byInd *) | ||
wIndex := 0; | (* start historical data from 0 *) | |
END_IF; (* C4 = TRUE *) |
At the end, the static inputs and outputs are processed, i.e.:
- reading the inputs,
- execution of actions
- writing the outputs.
(* Read inputs of DC541 *) | ||
DC541_IOs(EN := TRUE, SLOT := cbyDC541_SLOT ); | ||
DC541_IOs.OUT6 := DC541_IOs.IN5; | (* C6 := state of input channel C5 *) | |
DC541_IOs.OUT7 := NOT DC541_IOs.OUT7; | (* toggle channel C7 *) | |
(* Write outputs to DC541*) | ||
DC541_IOs(EN := TRUE, SLOT := cbyDC541_SLOT); |
Purpose of the cyclic program PLC_PRG:
The cyclic program PLC_PRG contains the following functions:
- Cycles counter dwC := dwC + 1;
- Reading the configuration of the DC541 Calling of block DC541_GET_CFG
- Reading the status of the DC541 Calling of block DC541_STATE - Reading/writing the static channels of the DC541 Calling of block DC541_IO
- Simulation of the interrupts for the DC541 Calling of block Simu_Pulse
The blocks DC541_GET_CFG, DC541_STATE and DC541_IO are contained in the library DC541_AC500_V11.lib and described in detail in the library documentation DC541_Library: DC541 Library
The block Simu_Pulse is used to generate an adjustable number of pulses. Its representation in the function block diagram (FBD) is as follows:
The meanings of the block’s inputs and outputs are as follows:
Instance | fbSimuPulse | Instance name | |
bEn | Input/Output | BOOL | Enabling of the pulse output |
bAutoReset | Input/Output | BOOL | Automatic reset of the pulse counter after the specified number of pulses have been output and after expiration of tResetTime |
bReset | Input/Output | BOOL | Reset of the pulse counter |
tResetTime | Input/Output | TIME | Time until the reset is initiated after the specified number of pulses is reached, if bAutoReset = TRUE |
dwPulse | Input/Output | DWORD | Number of pulses to be output: =0: Endless mode (pulse output continues until bEn = FALSE or bReset = TRUE > 0: Cyclic mode (output of the specified number of pulses) |
bDone | Output | BOOL | Completion message after tResetTime has expired or bReset = TRUE for 1 cycle |
bToggle_0 | Output | BOOL | Provides a FALSE->TRUE edge with each 2nd call (i.e. the output is toggled with each call) |
bToggle_1 | Output | BOOL | Provides a FALSE->TRUE edge with each 4th call |
bToggle_2 | Output | BOOL | Provides a FALSE->TRUE edge with each 8th call |
bToggle_3 | Output | BOOL | Provides a FALSE->TRUE edge with each 16th call |
dwActPulse | Output | DWORD | Displays the number of pulses output (corresponds to the number of edges at bToggle_0) |
tActTime | Output | TIME | Displays the elapsed time while tResetTime is running |
In the example, bEn: = bAutoReset: = TRUE. 10000 pulses are output (dwSetPulse). After the specified number of pulses has been reached, a wait time of 10 seconds is applied and then counting is started from the beginning.
The example has a visualization implemented which can be used to operate the program. After 10000 pulses, the visualization looks as follows:
9375 interrupts are generated:
5000 x C0 + 2500 x C1 + 1250 x C2 + 625 x C3 = 9375
Act Pulse | Triggers the following interrupts: | ||||
---|---|---|---|---|---|
Value | IN 3 8 |
IN 2 4 |
IN 1 2 |
IN 0 1 |
|
0 | 0 | 0 | 0 | 0 | none |
1 | 0 | 0 | 0 | 1 | IN 0 -> in every 2. cycle (10000 : 2 = 5000) |
2 | 0 | 0 | 1 | 0 | IN 1 -> in every 4. cycle (10000 : 4 = 2500) |
3 | 0 | 0 | 1 | 1 | IN 0 |
4 | 0 | 1 | 0 | 0 | IN 2 -> in every 8. cycle (10000 : 8 = 1250) |
5 | 0 | 1 | 0 | 1 | IN 0 |
6 | 0 | 1 | 1 | 0 | IN 1 |
7 | 0 | 1 | 1 | 1 | IN 0 |
8 | 1 | 0 | 0 | 0 | IN 3 -> in every 16. cycle (10000 : 16 = 625) |
9 | 1 | 0 | 0 | 1 | IN 0 |
10 | 1 | 0 | 1 | 0 | IN 1 |
11 | 1 | 0 | 1 | 1 | IN 0 |
12 | 1 | 1 | 0 | 0 | IN 2 |
13 | 1 | 1 | 0 | 1 | IN 0 |
14 | 1 | 1 | 1 | 0 | IN 1 |
15 | 1 | 1 | 1 | 1 | IN 0 |
16 | 0 | 0 | 0 | 0 | none |