LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Calculate and apply mask programatically

Solved!
Go to solution

Hello everyone,

 

I'm looking for a way to programmatically apply a mask to specific bits in a register.

 

To explain this better, here's an example:

I have a variable called Value1.
This variable, along with others, is registered in a Modbus map that includes additional information about the variables.
This information is:
- Variable type: B, U2, U3, ..., S64, S16, S32, F32, etc.
- The register number where the variable is located
- The bit positions occupied by the variable
- The variable name

 

In this case, let’s say the variable shares the same register with other variables, so:

(hexAdd)[b]:(cs):(cbr) decAdd (msb..lsb) type varName
(0x9c40)[0]:(16):(15..0) 40000 (15..0) U16 GlobalVariable
(0x9c40)[0]:(16):(0..0) 40000 (0..0) B Value0
(0x9c40)[0]:(16):(8..1) 40000 (8..1) U7 Value1
(0x9c40)[0]:(16):(9..9) 40000 (9..9) B Value2
(0x9c40)[0]:(16):(15..10) 40000 (15..10) U6 Value3

 

Therefore, to modify only the value of Value1, I would have to apply a mask 0xFE01 = 0b1111111000000001 and then add the value I want to set, for example, 0x00012 = 0b0000000000010010.
This way, only Value1 would be modified. Value0, Value2, and Value3 would remain unchanged.

 

Right now, every time I want to apply a mask, I have to calculate it and write it manually. This isn’t difficult and isn't a problem to me.
But sometimes, depending on the Modbus map I’m using, the same variable occupies a different location inside the register.
Therefore, I would like the mask to be applied to be calculated automatically based on the information provided by the Modbus map.

 

I have attached the VIs I am using, as well as the VI I have been testing. I am a bit stuck and unsure how to continue, so I would appreciate any guidance.

 

If I haven't made myself clear, please let me know and I will answer any questions.

 

Thanks in advance.

using LV 2017 on Win11
0 Kudos
Message 1 of 4
(233 Views)
Solution
Accepted by topic author xespizua

Well, I haven't looked at your code, but I figured it's a small fun challenge. I haven't done any real thinking and I wouldn't be surprised if others appear with cleaner solutions:

 

bitmask.png

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Some comments:

 

  1. In this example, I want to set the value 1101 starting at bit 10.
  2. When you do something like this, it help to take it one step at a time. That's what I did here with saying something like "I need a mask with N 1s (or 0s) in bit X. How do I get that?". Once I had that, I could keep going. In my example the number of bits corresponds to your variable type.
  3. If you can avoid it, I would suggest changing this structure. If the values in the registers are updated from more than one place, you have the potential for race conditions. Even if they're not, this tends to be harder to read. Unless you have real timing or space constraints, I would suggest separating the values in different registers (and have different blocks of registers for the different parties which can modify them).
  4. There is a Modbus function (22) which can modify only some bits in a register. It's not implemented by all devices or libraries, but if it works for you, it can remove the need to apply the mask yourself and mitigate the risk of race conditions (that depends on what the other side modifying the registers is doing, assuming of course there is something like that in the system).

___________________
Try to take over the world!
Message 2 of 4
(134 Views)

Hi tst,

 

First, thanks for your solution, it clarifies me some concepts and kinda worked.

 

But I find an issue in your example

If the variable is an U16 and all the bits fill the full register, the calculated mask is 0x0001 when it should be 0x0000.

 

I made some changes to fit with other VIs of mine and now works perfect.

 

xespizua_0-1782912777901.png

 

Thank you again.

using LV 2017 on Win11
0 Kudos
Message 3 of 4
(51 Views)

Hi xespizua,

 


@xespizua wrote:

I made some changes to fit with other VIs of mine and now works perfect.


Some comments:

AutoMask.vi:

  • in case "B" you calculate "u32(float(2^1))-1" instead of using the resulting value "1" as constant: any reason to obfuscate the constant?
  • in case "B" you subtract "1" from the MSB value, then you increment by one: any reason for that?

AutoValue.vi:

  • the same "subtract 1, then increment" limbo!?

I recommend to create the basic information for your Modbus variable directly in the GetVariableDataFromModbusMap VI. I would store the relevant information in a (typedefined) cluster, including:

  • a string to keep the variable name
  • a (typedefined) enum to hold the variable type
  • an integer holding the data size value (1 … 64)
  • an integer to hold the start bit (aka LSB)

You may keep an array of that cluster to hold the information for all variables. (Use maps/sets/variant attributes to have a LUT of those clusters based on the variable name.)

 

This way you have all needed information for any of the following data manipulation functions - and you need to generate this information only once instead in each of those subVIs. Parsing strings is "slow", so parse just once!

 

(I also prefer to create bitmasks based on the LSB instead of counting downwards from MSB. IMHO that's the usual way to handle bitfields.)

Best regards,
GerdW


using LV2016/2019/2021 on Win10/11+cRIO, TestStand2016/2019
0 Kudos
Message 4 of 4
(39 Views)