Explanations of the software of OpenDecoder

Block diagram of the software

  • dcc_receive
    Receive the DCC signal (interrupt-driven) and provide the received message.
  • dcc_decode
    analysis of the received message; if service mode or PoM: processing of the corresponding CV accesses
  • servo_engine
    Freely programmable servo controller with time/location mapping
  • port_engine
    Programmable controller for IO (with state machine, including flashing)

Interprocess communication

    OpenDecoder works in cooperative multitasking, whereby the time-critical parts are outsourced in interrupts. The message exchange between these modules takes place by means of semaphore operations. These are done under locked interrupts to make the operation thread safe:
  • There are a maximum of 8 semaphore bits, these are defined in config.h, eg C_Received or C_Save.
  • The sender of a message, for example, calls semaphore_set(C_Received). The associated semaphore bit is set.
  • The recipient of this message calls semaphor_get(C_Received). The associated semaphore bit queried and reset at the same time.
    For example:
    if (semaphor_get(C_Received)) analyze_received_message();
  • In addition, there is the method semaphor_query(C_Received) for * espionage purposes * , with which the semaphore can be queried, but it is not changed.
  • Parallel to these semaphore operations, which communicate the respective events, the message contents are transmitted via global variables (eg ReceivedAddr). This is not quite clean, but the time conditions are quite relaxed, so no mailbox system is required.

timing control

    In order to easily adapt the decoder to different applications, a general time control is provided for each output port. This timing controller uses three variables per output port:
      rest Remaining duration of the current state
      ontime Duration of the switch-on state
      offtime Duration of the switch-off state
    For timing control, an interrupt is now triggered in OpenDecoder every 25 ms (TICK_PERIOD). The ISR now checks the remaining duration of the current state: if this is zero, the output is left unchanged. If the remaining time is greater than zero, it is decremented by one. If this reaches zero, then depending on the current state of the port, the "ontime" or the "offtime" is reloaded. Now, if a switching action is to be performed, only the initial state of the port and the variables need to be properly loaded to achieve the desired behavior.

    • Port should be permanently on: port = on, rest = 0;
      "Rest = 0" prevents further switching of this port.
    • Port should output a single pulse with 0.5s: port = on, rest = 500ms, offtime = 0;
      The "rest" of 500ms causes a switchover and recharge after 500ms, after that no further switching takes place, since "rest" was reloaded with 0.
    • Port with 1s delayed pulse of 2s duration: Port = off, rest = 1000ms, ontime = 2000ms, offtime = 0
    • Change flasher:
      Port1 = on, rest = 500ms, ontime = 500ms, offtime = 500ms;
      Port2 = off, rest = 500ms, ontime = 500ms, offtime = 500ms
      After 500ms each time, reloading will take place, ontime + offtime together make the period of the flashing.
    • Running light, street light:
      Port1 = off, rest = 100ms, ontime = 25ms, offtime = 975ms;
      Port2 = off, rest = 150ms, ontime = 25ms, offtime = 975ms;
      Port3 = off, rest = 200ms, ontime = 25ms, offtime = 975ms;
      Port4 = off, rest = 250ms, ontime = 25ms, offtime = 975ms;
      Port5 = off, rest = 300ms, ontime = 25ms, offtime = 975ms;
      Port6 = off, rest = 350ms, ontime = 25ms, offtime = 975ms;
      Port7 = off, rest = 400ms, ontime = 25ms, offtime = 975ms;
      Port8 = off, rest = 450ms, ontime = 25ms, offtime = 975ms;
      All 8 ports have the same period of one second, but are started offset by 50ms. The short ontime produces the lightning effect.
    The durations specified in the source file are always given in µs. This sometimes gives quite large numbers (watch out for overflow) and requires the numbers to be marked with an after "L" as long (ie 32 bits). These are automatically converted to the necessary setting values of the time counters during compilation. TICK_PERIOD specifies the smallest time unit here, 255 times TICK_PERIOD (= 6.3s) is then the largest unit of time. For this reason, it is important that the correct CPU frequency is set during compilation (#define F_CPU).

Dimmer Control:

    The dimming control for the crossfading is done with an interrupt-controlled eight pulse width modulation (PWM) on eight channels. This is implemented as follows:
    • There are 60 brightness levels. The desired healing value per port is stored in dimm_val.
    • Every 300us there is an interrupt, this switches the dimmer one step further, after 60 steps it starts again from the beginning. At each stage, the following check is performed:
        MIN all ports with a dimm_val > DIMM_MIN are switched on.
        i A port whose dimm_val is less than x is turned off.
        MAX Restart and message to the DIMMER (C_Dimmstep).
    • Result: All ports with a dimm_val small DIMM_RANGE_MIN are permanently off, all ports with a dimm_val larger DIMM_RANGE_MAX are permanently on. All intermediate brightness values are output with a corresponding PWM.
    • Since the PWM has 60 levels and an interrupt occurs every 300us, this pass is made every 18ms. This corresponds to a refresh rate of 55Hz.
    When restarting the PWM, the DIMMER is triggered, which changes the dimm_val for the next pass depending on the currently set ramp and delay. For example, by slowly incrementing the dimm_val, a port can be dimmed.

    The ramp and delay are set for the normal signals with the function set_new_light_val; This function only needs to pass the bit field of the new states, an adaptation to different signal images should therefore be easily possible.

The way to your own decoder

    After installing the tools (AVR Studio, WinAVR and Ponyprog), open AVR-Studio and create a new project. Select as the simulation environment AVR-Simulator and ATtiny2313 processor. (accordingly Atmega8515 for OpenDecoder V2).

    Now set the clock rate and optimization hints for the compiler under Project->Configuration Options (note in particular -Os = optimize for size): Nun wird unter ProjectConfiguration Options die Taktrate und Optimierungshinweise für den

    Note: AVR Studio expects the input of the clock rate as a number (without UL), but then sets the entered number with appended UL (= unsigned long).

    The original file is then added with a right mouse to the tab Source-File and "add existing source file" to the project and now it will be translated without changing the sourcefile. This can be used to check that the environment is set up correctly.