4
\$\begingroup\$

I am currently developing a Modbus RTU enabled system that consists of DSP and a microprocessor that handles the Modbus communications. The DSP is sending data to the microprocessor and the microprocessor provides Modbus interface to the DSP results. The results may update at any moment in time (some are updated periodically, and other are updated at random intervals).

If the Modbus host decides to poll a set of registers that correspond to a single data set in several Modbus commands (we had an experience with one Modbus host that could not retrieve more then 32 registers at a time) there is a possibility that the data will be updated in-between the Modbus polls.

Is there any conventional way to handle scenarios like that in Modbus systems?

\$\endgroup\$
4
\$\begingroup\$

It sounds like you have a block of registers that must be read or written atomically (i.e. either read/write all of them, or read/write none of them). Traditionally how you deal with this is to have a set of shadow registers; the ISR fills or uses one table, and the UART subroutines use another. The routines which update the data structures lock the working set so that other routines don't get a partial update. Routines which must access the tables check the lock and either "skip their turn" or wait for the lock to be released.

You make these "critical sections" (sections where accessing the table is not allowed) as fast as possible, which is why I recommended a "shadow set" for the UART routines since they're probably the slowest things accessing the tables and in fact may be the only routines which must access the whole set as a single unit. You don't lock the table until you are absolutely sure that you need to, and then you lock it only for the length of time that it takes to copy the data.

Something like this:

if (command_is_good && cmd_is_request) {
    lock_working_table();
    memcpy(serial_copy, working_copy, sizeof(*working_copy);
    unlock_working_table();
    prepare_response(serial_copy);
}

if (command_is_good && cmd_is_update) {
    if (verify_table(serial_copy)) {
        lock_working_table();
        memcpy(working_copy, serial_copy, sizeof(*working_copy));
        unlock_working_table();
        prepare_response();
     }
}

You can get similar results by disabling the ADC interrupt, but I've found over the years that there are generally very, very few times you want to actually screw with interrupt enables, and this is almost never one of them. Locking and better design are almost always the proper way to handle this, which is why I added the comment to @kvegaoro's post. It messes with your timing which may or may not be important, and if you disable the UART ISR you could overflow your FIFO.

If you find that you have a large set of registers that must be updated atomically you may also want to break them up in to smaller atomic groups and limit you request size or bounds.

Edit to address @DaveTweed's comment:

As far as a convention used by ModBus devices... The best thing about ModBus is also the worst thing about ModBus; you are free to do things however you like. The specification is very "loose" in that respect. There is no convention that I'm aware of, but ModBus devices which have such a requirement about blocks of registers would have to implement things similar to how I described the solution above. Almost all trivial devices limit you to a single register for read/write operations, neatly sidestepping the issue. :-)

\$\endgroup\$
  • \$\begingroup\$ Good answer, but I think the OP's problem is a bit more subtle than that: He wants to know what to do when the Modbus host can't read the entire set of registers in a single transaction for some reason. In that case, how do you decide when to lock/unlock the set of registers? Is there a Modbus convention for dealing with this? \$\endgroup\$ – Dave Tweed Dec 21 '12 at 12:29
  • \$\begingroup\$ @DaveTweed that is correct. The issue I am having is that there is one specific type of DSP data packets that are generated randomly and may, theoretically be streamed from the DSP 99% of the time. These packets are also quite large and are well above the modbus 256 bytes frame size. \$\endgroup\$ – udushu Dec 22 '12 at 21:21
  • \$\begingroup\$ @AndrewKohlsmith messing with the interrupt vector and/or enable registers is indeed a bit radical. My situation is also complicated by the fact that I have "hardish" real-time requirements due to the data stream from the DSP, however I ended up implementing something similar and currently contemplating a feature that would keep the DSP data blocked for some time after a modbus poll... this however introduces additional difficulty with possible scenario of constant modbus polls and a need to throttle the comm. requests. \$\endgroup\$ – udushu Dec 22 '12 at 21:27
  • \$\begingroup\$ I would rethink your register implementation; it's unreasonable to require atomic reads/writes of more than 256 registers. Shadow registers followed by an "update" coil to actually put those new values into service is perhaps a workable solution. \$\endgroup\$ – akohlsmith Dec 22 '12 at 22:46
0
\$\begingroup\$

In the systems that I have worked with Data coming from DSP or external high precision ADC is usually interrupt driven (I'm also assuming you system is interrupt driven since you say that data may come in at any time). Usually when you received a Modbus query you might want to disable interrupts momentarily while you handle the query and enable the interrupts again after the query has been handle. Once you enable the interrupts all the pending interrupt requests will be handled. This way you ensure that values are not update in the middle of a query and end up with the incorrect results.Hope this helps

\$\endgroup\$
  • 4
    \$\begingroup\$ yikes, definitely do not do that. There should be no need to mess with interrupts. See my answer. \$\endgroup\$ – akohlsmith Dec 21 '12 at 0:21
0
\$\begingroup\$

I've seen modbus devices implement a command that was ~"lock values for me til I say so please" to allow for this sort of behaviour. They often used a second register range for this locked set. The master could do then a sequence of

  1. write_register (lock command)
  2. read register(s) 1..n
  3. continue until finished
  4. write_register (unlock command)

I've seen this even implemented as a broadcast write to allow synchronising across devices as well.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.