【C#】NI-VISAでファンクションジェネレータをリモート制御してみた

C# NI-VISAを使ってファンクションジェネレータを制御

最近、在宅ワークが増えたため出社して作業場で仕事する機会も減ってきました。

家で快適にプログラミングできるのは心地よいのですが、仕事柄オシロスコープやファンクションジェネレータを使うためやりにくい部分もあります。

そこで、今回はC#でNI-VISAというサポート製品を使ってファンクションジェネレータをUSB接続してリモート制御する方法を紹介していきます。

この記事はこんな人におすすめ
  • ファンクションジェネレータなどの計測器をUSB接続してリモート制御したい
  • NI-VISAのプログラミング例を知りたい
目次

NI-VISAって何?

NI-VISAは「Virtual Instrument Software Architecture」の略で、ナショナルインスツルメンツ(National Instrument)が開発したAPIになります。

簡単にいうとNI-VISAを使えば、Ethernet、​GPIB、​シリアル、​USB​など​の​計測​器を制御するプログラミングが簡単にできるというサービスになります。

NI-VISAのダウンロードページからインストールすることでAPIを使用できるようになります。

NI-VISAのAPIを使ったC#コード

NI-VISAのインストール手順は省かせてもらって、早速プログラミングしていきたいと思います。

今回制御するファンクションジェネレータ
  • エヌエフ回路設計ブロックのWF1948(USB接続でリモート制御)

USB機器の接続

USB接続機器と繋げるためにリソースを検索する作業をしていきます。

次のようなフォームを作成します。

ListBoxに接続可能なデバイスが表示されるので、対象のファンクションジェネレータを選んで接続するというものです。

サンプルコード

using NationalInstruments.Visa;
using System;
using System.Windows.Forms;

namespace FunctionGeneratorController
{
    public partial class SelectResource : Form
    {
        public SelectResource()
        {
            InitializeComponent();
        }

        private void SelectResource_Load(object sender, EventArgs e)
        {
            // This example uses an instance of the NationalInstruments.Visa.ResourceManager class to find resources on the system.
            // Alternatively, static methods provided by the Ivi.Visa.ResourceManager class may be used when an application
            // requires additional VISA .NET implementations.
            using (var rmSession = new ResourceManager())
            {
                var resources = rmSession.Find("(ASRL|GPIB|TCPIP|USB)?*");
                foreach (string s in resources)
                {
                    listBoxAvailableResources.Items.Add(s);
                }
            }
        }

        private void listBoxAvailableResources_DoubleClick(object sender, EventArgs e)
        {
            string selectedString = (string)listBoxAvailableResources.SelectedItem;
            ResourceName = selectedString;
            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        private void listBoxAvailableResources_SelectedValueChanged(object sender, EventArgs e)
        {
            string selectedString = (string)listBoxAvailableResources.SelectedItem;
            ResourceName = selectedString;
        }

        public string ResourceName
        {
            get
            {
                return textBoxVisaResourceName.Text;
            }
            set
            {
                textBoxVisaResourceName.Text = value;
            }
        }        
    }
}

まず、NI-VISAのAPIを使うためにNationalInstruments.Visaを呼び出しましょう。

using NationalInstruments.Visa;

次に、USB接続機器をオープンするための処理を加えます。

using (var rmSession = new ResourceManager())
{
    var resources = rmSession.Find("(ASRL|GPIB|TCPIP|USB)?*");
    foreach (string s in resources)
    {
        listBoxAvailableResources.Items.Add(s);
    }
}

ファンクションジェネレータにASCIIデータを送って制御してみる

続いて、ファンクションジェネレータを制御するためのコマンドを実装していきます。

まず、使用コマンドを定義していきます。

サンプルコード

using System;
using System.Collections;
using System.Collections.Generic;

namespace FunctionGeneratorController
{
    /// <summary>
    /// 使用コマンドを定義
    /// </summary>
    public class SubSystemCommand : IEnumerable<string>
    {
        /// <summary>
        /// SOURCE
        /// </summary>
        public string SOURCE { get; set; } = "SOURce";
        /// <summary>
        /// OUTPUT
        /// </summary>
        public string OUTPUT { get; set; } = "OUTPut";
        /// <summary>
        /// SYSTEM
        /// </summary>
        public string SYSTEM { get; set; } = "SYSTem";

        /// <summary>
        /// ROOTコマンド
        /// </summary>
        /// <returns></returns>
        public IEnumerator<string> GetEnumerator()
        {
            yield return SOURCE;
            yield return OUTPUT;
            yield return SYSTEM;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// SOURCE
        /// </summary>
        public class Source : IEnumerable<string>
        {
            /// <summary>
            /// VOLTAGE
            /// </summary>
            public string VOLTAGE { get; set; } = "VOLTage";
            /// <summary>
            /// FREQUENCY
            /// </summary>
            public string FREQUENCY { get; set; } = "FREQuency";
            /// <summary>
            /// PHASE
            /// </summary>
            public string PHASE { get; set; } = "PHASe";

            /// <summary>
            /// SOURCEコマンド
            /// </summary>
            /// <returns></returns>
            public IEnumerator<string> GetEnumerator()
            {
                yield return VOLTAGE;
                yield return FREQUENCY;
                yield return PHASE;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                throw new NotImplementedException();
            }

            public class Voltage : IEnumerable<string>
            {
                /// <summary>
                /// LEVEL
                /// </summary>
                public string LEVEL { get; set; } = "LEVel";
                /// <summary>
                /// IMMEDIATE
                /// </summary>
                public string IMMEDIATE { get; set; } = "IMMediate";
                /// <summary>
                /// AMPLITUEDE
                /// </summary>
                public string AMPLITUEDE { get; set; } = "AMPLitude";
                /// <summary>
                /// OFFSET
                /// </summary>
                public string OFFSET { get; set; } = "OFFSET";
                public IEnumerator<string> GetEnumerator()
                {
                    yield return LEVEL;
                    yield return IMMEDIATE;
                    yield return AMPLITUEDE;
                    yield return OFFSET;
                }

                IEnumerator IEnumerable.GetEnumerator()
                {
                    throw new NotImplementedException();
                }
            }

            public class Frequency : IEnumerable<string>
            {
                /// <summary>
                /// FIXED
                /// </summary>
                public string FIXED { get; set; } = "FIXed";
                /// <summary>
                /// CW
                /// </summary>
                public string CW { get; set; } = "CW";
                public IEnumerator<string> GetEnumerator()
                {
                    yield return FIXED;
                    yield return CW;
                }

                IEnumerator IEnumerable.GetEnumerator()
                {
                    throw new NotImplementedException();
                }
            }
        }

        /// <summary>
        /// OUTPUT
        /// </summary>
        public class Output : IEnumerable<string>
        {
            /// <summary>
            /// STATE
            /// </summary>
            public string STATE { get; set; } = "STATe";            

            /// <summary>
            /// OUTPUTコマンド
            /// </summary>
            /// <returns></returns>
            public IEnumerator<string> GetEnumerator()
            {
                yield return STATE;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// FUNCTION
        /// </summary>
        public class Function : IEnumerable<string>
        {
            /// <summary>
            /// ON
            /// </summary>
            public string ON { get; set; } = "ON";
            /// <summary>
            /// OFF
            /// </summary>
            public string OFF { get; set; } = "OFF";

            public IEnumerator<string> GetEnumerator()
            {
                yield return ON;
                yield return OFF;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                throw new NotImplementedException();
            }
        }
    }
}

次に、コマンド送信用のクラスを作成していきます。

サンプルコード

using NationalInstruments.Visa;
using System;

namespace FunctionGeneratorController
{
    public class Control
    {
        SubSystemCommand sysCmd = new SubSystemCommand();
        SubSystemCommand.Source source = new SubSystemCommand.Source();
        SubSystemCommand.Output output = new SubSystemCommand.Output();
        SubSystemCommand.Function function = new SubSystemCommand.Function();
        SubSystemCommand.Source.Voltage voltage = new SubSystemCommand.Source.Voltage();
        SubSystemCommand.Source.Frequency frequency = new SubSystemCommand.Source.Frequency();

        /// <summary>
        /// チャネル番号
        /// </summary>
        enum Channel
        {
            One = 0,
            Two
        }

        /// <summary>
        /// チャネル出力状態
        /// </summary>
        enum ChannelState
        {
            OFF = 0,
            ON
        }

        /// <summary>
        /// コマンドを送信する
        /// </summary>
        /// <param name="ch"></param>
        /// <param name="cmds"></param>
        /// <param name="func"></param>
        public void SendCommand(MessageBasedSession mbSession, int ch, string[] cmds, string func)
        {
            var text = string.Empty;

            foreach(var cmd in cmds)
            {
                text += ":";
                text += cmd;
            }

            if (func != "?")
            {
                text += " " + func;
            }
            else
            {
                text += func;
            }

            mbSession.RawIO.Write(text);
        }

        /// <summary>
        /// 指定チャネルの出力をONにする
        /// </summary>
        /// <param name="mbSession">セッション</param>
        /// <param name="channel">対象チャネル</param>
        public void FuncON(MessageBasedSession mbSession, int channel)
        {
            try
            {
                var cmds = new string[] { sysCmd.OUTPUT + channel.ToString(), output.STATE };
                var func = function.ON;
                SendCommand(mbSession, channel, cmds, func);                
            }
            catch (Exception ex)
            {
            }
        }

        /// <summary>
        /// 指定チャネルの出力をOFFにする
        /// </summary>
        /// <param name="mbSession">セッション</param>
        /// <param name="channel">対象チャネル</param>
        public void FuncOFF(MessageBasedSession mbSession, int channel)
        {
            try
            {
                var cmds = new string[] { sysCmd.OUTPUT + channel.ToString(), output.STATE };
                var func = function.OFF;
                SendCommand(mbSession, channel, cmds, func);
            }
            catch (Exception ex)
            {
            }
        }

        /// <summary>
        /// 振幅を設定する
        /// </summary>
        /// <param name="mbSession"></param>
        /// <param name="channel"></param>
        /// <param name="value"></param>
        /// <param name="unit"></param>
        public void SetAmplitude(MessageBasedSession mbSession, int channel, float value, string unit)
        {
            try
            {
                var cmds = new string[] { sysCmd.SOURCE + channel.ToString(), source.VOLTAGE, voltage.LEVEL, voltage.IMMEDIATE, voltage.AMPLITUEDE };
                var func = value.ToString() + unit;
                SendCommand(mbSession, channel, cmds, func);
            }
            catch (Exception ex)
            {
            }
        }

        /// <summary>
        /// 周波数を設定する
        /// </summary>
        /// <param name="mbSession"></param>
        /// <param name="channel"></param>
        /// <param name="value"></param>
        /// <param name="unit"></param>
        public void SetFrequency(MessageBasedSession mbSession, int channel, float value, string unit)
        {
            try
            {
                var cmds = new string[] { sysCmd.SOURCE + channel.ToString(), source.FREQUENCY, frequency.CW };
                var func = value.ToString() + unit;
                SendCommand(mbSession, channel, cmds, func);
            }
            catch (Exception ex)
            {
            }
        }

        /// <summary>
        /// オフセットレベルを設定する
        /// </summary>
        /// <param name="mbSession"></param>
        /// <param name="channel"></param>
        /// <param name="value"></param>
        public void SetDcOffset(MessageBasedSession mbSession, int channel, float value)
        {
            try
            {
                var cmds = new string[] { sysCmd.SOURCE + channel.ToString(), source.VOLTAGE, voltage.LEVEL, voltage.IMMEDIATE, voltage.OFFSET};
                var func = value.ToString();
                SendCommand(mbSession, channel, cmds, func);
            }
            catch (Exception ex)
            {
            }
        }

        /// <summary>
        /// 指定チャネルのON/OFF状態をチェック
        /// </summary>
        /// <param name="mbSession"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        public bool ReadFuncOnOff(MessageBasedSession mbSession, int channel)
        {
            try
            {
                var cmds = new string[] { sysCmd.OUTPUT + channel.ToString(), output.STATE };
                var func = "?";
                SendCommand(mbSession, channel, cmds, func);
                var returnValue = RemoveCommonEscapeSequences(mbSession.RawIO.ReadString());

                if (Convert.ToInt32(returnValue) == (int)ChannelState.OFF) return false;

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        /// <summary>
        /// 周波数を読み込む
        /// </summary>
        /// <param name="mbSession"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        public float ReadFrequency(MessageBasedSession mbSession, int channel)
        {
            try
            {
                var cmds = new string[] { sysCmd.SOURCE + channel.ToString(), source.FREQUENCY, frequency.CW };
                var func = "?";
                SendCommand(mbSession, channel, cmds, func);
                var returnValue = RemoveCommonEscapeSequences(mbSession.RawIO.ReadString());

                float outValue = 0;
                float.TryParse(returnValue, out outValue);

                return outValue;
            }
            catch (Exception ex)
            {
                return 0;
            }
        }

        /// <summary>
        /// 振幅値を読み込む
        /// </summary>
        /// <param name="mbSession"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        public float ReadAmplitude(MessageBasedSession mbSession, int channel)
        {
            try
            {
                var cmds = new string[] { sysCmd.SOURCE + channel.ToString(), source.VOLTAGE, voltage.LEVEL, voltage.IMMEDIATE, voltage.AMPLITUEDE };
                var func = "?";
                SendCommand(mbSession, channel, cmds, func);
                var returnValue = RemoveCommonEscapeSequences(mbSession.RawIO.ReadString());

                float outValue = 0;
                float.TryParse(returnValue, out outValue);

                return outValue;
            }
            catch (Exception ex)
            {
                return 0;
            }
        }

        /// <summary>
        /// オフセット電圧を読み込む
        /// </summary>
        /// <param name="mbSession"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        public float ReadOffset(MessageBasedSession mbSession, int channel)
        {
            try
            {
                var cmds = new string[] { sysCmd.SOURCE + channel.ToString(), source.VOLTAGE, voltage.LEVEL, voltage.IMMEDIATE, voltage.OFFSET };
                var func = "?";
                SendCommand(mbSession, channel, cmds, func);
                var returnValue = RemoveCommonEscapeSequences(mbSession.RawIO.ReadString());

                float outValue = 0;
                float.TryParse(returnValue, out outValue);

                return outValue;
            }
            catch (Exception ex)
            {
                return 0;
            }
        }

        /// <summary>
        /// 改行文字を削除
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private string RemoveCommonEscapeSequences(string s)
        {
            return s.Replace("\n", string.Empty).Replace("\r", string.Empty);
        }

    }
}

この中のSendCommand(mbSession, channel, cmds, func)関数を使うことで次のことができるようになります。

  • 出力チャネルの選択
  • 周波数設定
  • 振幅設定
  • オフセット電圧設定

使用コマンドを増やしたい場合はSubSystemCommand.cs内のコマンドを定義することで拡張可能です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

都内の精密機器を作っている会社に勤務している14年目のエンジニアです。趣味は美味しいものを食べることとゴルフ。プログラムについて今まで学んだことをわかりやすく発信するサイトを目指しています。

コメント

コメントする

目次
閉じる