Search code examples
labviewbcdpacked

How can I write packed BCD numbers to an old instrument (Varian FR41 Gaussmeter) with LabVIEW?


I am currently trying to communicate with an old instrument that uses packed BCD numbers only (it's a Varian FR41 Controller Gaussmeter if anyone is familiar). I am using LabVIEW to replicate some old C code that output the BCD code. Below is the C script. What it is doing is some simple arithmetic to generate integers that are exported to the instrument through the IEEE port using %c.

#include "ieeeio.h"
#include <math.h>
#include <stdio.h>


main()

{
  long  temp;
  int   z1,z2,z3,z4,b1,b2,b3;
  float b;
  double gauss,hv=5000.,magconst=.069,Mass=87.;

  if (ieeeinit()==-1)
  {
    printf("Cannot initialize IEEE system.\n");
    exit(1);
  }
  gauss=sqrt(Mass*hv/magconst);
  temp=10*gauss;
  b=temp/10.;
  z1=b/1000;
  z2=b/100;
  z3=b/10;
  z4=b;
  printf("\n\r %f %f %d %d %d %d",gauss,b,z1,z2,z3,z4);
  b1=z2+z1*6;
  b2=z4+6*z3-160*z2;
  b3=(b-z4)*160+14;
  printf("\n\r %d %d %d %d",7,b1,b2,b3);
  ieeewt("output 08;");
  ieeeprtf("%c%c%c%c\n",7,b1,b2,b3);
}

I have generated a .VI that generates the same numbers b1,b2,b3 and puts them into a string of packed BCD numbers. Using the input variables HV,magconst,mass above, the output should be 2510.8 gauss. and b1,b2,b3 are 142,37,16 respectively.

Now on to what the Varian Gaussmeter wants. This is the text that describes how the instrument receives information (not all of this is useful, but I copy it for the sake of completeness). I'm also attaching an image of the schematic that shows the BCD format. Format for BCD from manual

Data are transmitted to the FR-41 as a sequence of three 8 bit bytes. Each byte is divided into two half bytes, hi and lo, which may contain BCD numbers only. When auxiliary output port is used a fourth byte (any binary number) is added to the beginning of the sequence. The last character sent contains the least significant digit (LSD) of the new Gauss setting in the hi byte location and a load control NON BCD character, (bin 14) in the lo byte which is interpreted to load all registers with the new data. The one bit of the lo byte controls the 10 kGauss overrange when used. no change of setting occurs until the control word is received.

So, if I'm understanding this correctly in the C code above, the first byte added (aux output port thing) seems to be 7, which should be 0000 0111 in BCD. The following bytes, to write 2510.8, should be 0010 0101 0001 0000 1000 followed by 1110 (for the bin 14 control word).

Here's what I have done. Using the attached .VI, I can reproduce the 32 bit BCD string that I expect it should be. I'm sending this to the GPIB Send Message VI in LabVIEW (I've also tried using VISA Write, but it does the exact same thing across all tests as far as I can see). However, the instrument doesn't register anything happening.

The interesting thing is that when I send the instrument the information as a %f string, of floating points, so, 7.0000142.00037.00016.000, the instrument registers something happening and the tens and hundreds places change. So, if I send 42 as a floating point, the instrument will go to X4X2.X gauss setting. If I put 142 it will do the same thing. If I put 17 it will go to X1X7.X setting. If I send it 17 then 25 it will only take the first one.

My thought is that something in the GPIB Send or VISA Write is taking my string of BCD numbers and sending them out as something else, but I can't figure out what I could be doing wrong. Any help or tests would be much appreciated; I've been struggling with this for weeks now to no avail. VI for reproducing the C script copied above


Solution

  • As you wrote, the value 2510.8 should be send with a leading byte of value 7 and a tailing control nibble of value 14 as

    0000'0111 0010'0101 0001'0000 1000'1110

    But you are expected to send exactly 4 Bytes, not this long string.

    Just as hint: The above in hexadecimal representation is

    0x0725108E

    Note how the 2510.8 appears as 25108 here. That's the magic of BCD.


    Now, it took me a while to understand your LV code. Finally, you convert that value of 2510.8 into a string "2510.8", and then extract the digits.

    First problem: You extract a string of length 1 from the beginning (="2") for z1, a string of length 2 from the beginning (="25") for z2, a string of length 3 from the beginning (="251") for z3... (="2510") for z4. You need to increment the starting position, and keep the length =1 for the substring method.

    Second: You try to format the values as binary into a string.

    The following code converts value b into the desired string. (It is a VI-snippet, that is: Open a new VI, and drag this picture from this website directly into the block diagramm window!)

    enter image description here

    In detail:

    • Multiply b by 10 to make it integer
    • Divide by 10 five times in a loop, and create an array of the remainders.
      The remainders are the nibbles!
    • revert the direction of the array, and append a 14 as last nibble.
    • reshape as 2D array, so in each row stand the upper and the lower nibble
    • loop over rows, build bytes of the left and right colums (upper and lower nibble)
    • Insert a byte 7 at the beginning of the new array
    • use ByteArrayToString to get what you need to send to the device

    Note:

    • This is one solution, there are for sure faster and better ones. But this is easy.
    • This solution can convert any (integer) number to BCD, while the C-code is very specific to this single use case
    • The U8 in the first loop is necessary, since the ToString function can also convert 4byte numerical variables to 4byte strings, but we need 1byte.
    • The BCD Bytes indicator has been set to display as HEX, this has nothing to do with the values
    • The string indicator shows 4 cryptic bytes, because not all bytes are ASCII characters. This is fully OK!