From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

NI製品ディスカッション

キャンセル
次の結果を表示 
次の代わりに検索 
もしかして: 

USB-8502/1 (NI-XNET)をC#からnixnet.dllを使ってCAN通信を行いたい

はじめまして、USB-8502/1を購入しました

VisualC#からUSB-8502/1(nixnet.dll)を介してCAN通信を行いたいと思っています

このディスカッションの「NI-CAN VisualC#対応」が参考になるのは承知しておりますが、

資料が少なく非常に難航しています

NI-CANでは、

https://forums.ni.com/t5/Automotive-and-Embedded-Networks/Does-NI-CAN-USB-8473-support-programming-i...

辺りが参考になりますがNI-XNETは新しい技術ということもあってか情報が殆ど

ありません

当方、CANの知識も経験もないため易しめのサンプルがあると助かるのですが、

お薦めの文献などありましたら紹介いただきたいと思います

先ずは固定フレームの定期送信から始めたいと考えています

また、nixnet.hにも情報があるようですが残念ながら当該ファイルの入手方法も

わかっていません

よろしくお願いします

0 件の賞賛
メッセージ1/10
7,028件の閲覧回数

newon1さん、

最新版NI-XNETもNI-CANもC#などの.NET環境を正式にサポートしていないようです。

http://download.ni.com/support/softlib//embedded%20networks/NI-XNET/Driver/17.0.1/readme_jpn.htm

ご紹介なさっているフォーラムページではNI-CANのC APIをC#から呼び出すために独自に作成したラッパーライブラリを使っているようです。XNETでもそういったことができるのかもしれませんが、Cであればサンプルもありますし、可能であればCで作成するほうがよいのではないかと思います。

NI-XNETドライバをインストールするときにC言語サンプルをインストールするように選択すれば、

C:\Users\Public\Documents\National Instruments\NI-XNET\Examples\MS Visual C\CAN

にCANのC言語サンプルがインストールされるようです。

 

nixnet.hは

C:\Program Files (x86)\National Instruments\Shared\ExternalCompilerSupport\C\include

nixnet.libは

C:\Program Files (x86)\National Instruments\Shared\ExternalCompilerSupport\C\lib32\msvc

にあります。

0 件の賞賛
メッセージ2/10
6,969件の閲覧回数

情報ありがとうございます

色々調べてみたところ、「LabWindows/CVIサポート」を選択すると次の場所でも

見つけられました

C:\Program Files\National Instruments\Shared\CVI\include\nixnet.h

Cサンプルも見てはいますが、そのまま実行できるのかよくわかりません

当面、C#で継続して行きます(Cが不得手というのも理由です)

0 件の賞賛
メッセージ3/10
6,943件の閲覧回数

CサンプルはVisual BasicなどのIDEで読み込んでEXEを作る必要がありますね。そのときには.hファイルと.libファイルを参照項目に入れてからビルドをします。C#で継続なされるというのは、なにか良い方法を見つけられたのでしょうか?

0 件の賞賛
メッセージ4/10
6,934件の閲覧回数

引き続き情報収集を進めております

全体を統括するプログラムがC#(WPF)なのでNI-XNETもC#内で処理したいという狙いです

現在参考にしているのが、

http://forums.ni.com/t5/Automotive-and-Embedded-Networks/Net-wrapper-for-XNET-API/td-p/3249494

http://forums.ni.com/t5/Automotive-and-Embedded-Networks/Using-X-NET-C-API/td-p/2391466

辺りです

手始めにnixnet.dllと会話するとこから研究しています

DLL内の関数を処理した際に出力されるステータスを文章に変換してくれる

nxStatusToStringの取り込みをしてみました

NI-CANのncStatusToStringと全く同じ呼び出しですが、CallingConventionを

省略すると正しくないみたいです(スタックが不安定~とか不気味な警告が出ます)

[DllImport("Nixnet.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void nxStatusToString(
                                        int Status,                //nxStatus_t
                                        uint SizeofString,        //u32
                                        StringBuilder StatusDescription        //char*
                                        );

使い方は、

int status = 0;

StringBuilder status_str = new StringBuilder(1024);

nxStatusToString(status, (uint)(status_str.Capacity), status_str);

みたいな感じで、status_strの中に解説が書き込まれます

当面の目標が固定フレームの定期送信なのでセッション生成が必要だと思います

次のような感じで一応動くっぽいです

呼び出し関数は参考サイトを見て

[DllImport("Nixnet.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int nxCreateSession(
                                        string DatabaseName,        //const char*
                                        string ClusterName,            //const char*
                                        string List,                //const char*
                                        string Interface,            //const char*
                                        uint Mode,                    //u32
                                        out IntPtr SessionRef        //nxSessionRef_t*
                                        );

という感じにしました

int status = 0;

string DBName = "nixnet_example";

string CluName = "CAN_Cluster";

string SignalArray = "TestSignal1,TestSignal2";

string IFName = "CAN0";

status = nxCreateSession(DBName, CluName, SignalArray, IFName, 11, out SessionRef);

という感じで構文エラーは出ていないみたいです

まだ実機で試していないので正しくないかもしれません

なお、Modeの11はnxMode_FrameOutSinglePointのことです

また、DatabaseName、ClusterName、Listに代入する内容がまだわかっていないので

上記では適当な文字を入れています

 

上記内容への指摘や情報を提供頂けますと幸いです

0 件の賞賛
メッセージ5/10
6,880件の閲覧回数

次の様なサイトを見つけましたが、残念ながらそのまま使えない感じでした

http://www.seqzap.com/SeqZapManuals/SeqZapTools/ni-xnet.html

https://github.com/seqzap/ni-xnet-dot-net

 

前回の続きですが、DatabaseName、ClusterName、Listに適当な名前は使えませんでした

エラーメッセージは、

「フレームはデータベースで見つかりませんでし た。解決策: データベースで

定義されたセッションのフレームのみを初期化して いることを確認してください。」
との内容です

NI-XNETではデータベースがないと何もできないっぽいです

先ずはメモリデータベースを研究してみました

参考にしたサイトは次です

http://forums.ni.com/t5/LabVIEW/ni-xnet-problem-with-the-in-memory-database-creation/td-p/2481714?db...

 

呼び出し関数は次の3つで行けそうです

[DllImport("Nixnet.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int nxdbOpenDatabase(
                                        string DatabaseName,    //const char*
                                        out uint DatabaseRef    //nxDatabaseRef_t*(=u32*)
                                        );

[DllImport("Nixnet.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int nxdbCreateObject(
                                        uint DatabaseRef,        //nxDatabaseRef_t(=u32)
                                        uint ObjectClass,        //u32
                                        string ObjectName,        //const char*
                                        out uint DbObjectRef    //nxDatabaseRef_t*(=u32*)
                                        );

[DllImport("Nixnet.dll",  CallingConvention = CallingConvention.Cdecl)]
public static extern int nxdbSetProperty(
                                        uint DbObjectRef,        //nxDatabaseRef_t(=u32)
                                        uint PropertyID,        //u32
                                        uint PropertySize,        //u32
                                        [MarshalAs(UnmanagedType.AsAny)] object PropertyValue    //void*
                                        );

メモリデータベースでは名前として「:memory:」を使うみたいです

データベースの下にクラスタ、その下にフレーム、その下に信号(シグナル)を作ることになる感じです

以下の要領で作成ができるみたいです

今回はフレームまでで止めています

static uint DatabaseRef;

static uint ClusterRef;

static uint FrameRef;

string DBName = ":memory:";

string CluObject = "CAN_Cluster";

string FrObject = "CAN_Frame";

int status = 0;

//データベースオープン

status = nxdbOpenDatabase(DBName, out DatabaseRef);

//クラスタ作成(0x00010000)

status = nxdbCreateObject(DatabaseRef, 0x00010000, CluObject, out ClusterRef);

//フレーム作成(0x00020000)

status = nxdbCreateObject(ClusterRef, 0x00020000, FrObject, out FrameRef);

//クラスタ通信速度設定(0x00010001)

uint BaudRate = 500000;
status = nxdbSetProperty(ClusterRef, 0x00010001, 4, BaudRate);

//フレーム拡張ID設定(0x02020010)

byte ExtID = 1;
status = nxdbSetProperty(FrameRef, 0x02020010, 1, ExtID);

//フレームID設定(0x00020003)

uint FrameID = 357827584;
status = nxdbSetProperty(FrameRef, 0x00020003, 4, FrameID);

//フレームペイロード長設定(0x00020007)

uint PayloadLength = 2;
status = nxdbSetProperty(FrameRef, 0x00020007, 4, PayloadLength);

//フレームタイミングタイプ設定(0x00020011)

uint TimingType = 0;
status = nxdbSetProperty(FrameRef, 0x00020011, 4, TimingType);

//フレーム送信時間間隔設定(0x01020012)

double TransmitTime = 0.1;
status = nxdbSetProperty(FrameRef, 0x01020012, 8, TransmitTime);

//フレームデフォルトペイロード設定(0x0A020005)

nxCAN_Payload DefaultPayload = new nxCAN_Payload();
DefaultPayload.Payload0 = 115;
DefaultPayload.Payload1 = 14;
status = nxdbSetProperty(FrameRef, 0x0A020005, 2, DefaultPayload);

 

PropertyIDのOR計算に気付かず、だいぶ迷走しました

問題はこのデータベースをどうやって送信に結びつけるかという点です

FrameOutSinglePoint、FrameOutQueued、FrameOutStreamの使い分けと使い方が

まだわかっていません

 

上記内容への指摘や情報を提供頂けますと幸いです

 

※nxCAN_Payloadは1Du8で、以下定義としました

[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct nxCAN_Payload    // size of structure is 8 bytes
 {
            [FieldOffset(0)]
            public byte Payload0;        //u8
            [FieldOffset(1)]
            public byte Payload1;        //u8
            [FieldOffset(2)]
            public byte Payload2;        //u8
            [FieldOffset(3)]
            public byte Payload3;        //u8
            [FieldOffset(4)]
            public byte Payload4;        //u8
            [FieldOffset(5)]
            public byte Payload5;        //u8
            [FieldOffset(6)]
            public byte Payload6;        //u8
            [FieldOffset(7)]
            public byte Payload7;        //u8
 }

0 件の賞賛
メッセージ6/10
6,830件の閲覧回数

メモリデータベースが一応できたので、送信を試みました

先ずはFrameOutSinglePointを使ってみました

呼び出し関数は次の2つで行けそうです

[DllImport("Nixnet.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int nxCreateSession(
                                        string DatabaseName,    //const char*
                                        string ClusterName,        //const char*
                                        string List,            //const char*
                                        string Interface,        //const char*
                                        uint Mode,                //u32
                                        out uint SessionRef        //nxSessionRef_t*(=u32*)
                                        );

[DllImport("Nixnet.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int nxWriteFrame(
                                        uint SessionRef,                //nxSessionRef_t(=u32)
                                        [MarshalAs(UnmanagedType.AsAny)]object Buffer,    //void*
                                        uint NumberOfBytesForFrames,    //u32 (byte)
                                        double Timeout                    //f64 (second)
                                        );

この関数にメモリデータベースを代入しました

int status = 0;
string DBName = ":memory:";
string CluName = "CAN_Cluster";
string Array = "CAN_Frame";

string IFName = "CAN1";

//セッション作成

status = nxCreateSession(DBName, CluName, SignalArray, IFName, 11, out SessionRef);

//送信フレーム(Payload長2、拡張ID=357827584)作成
nxFrameCAN TestFrame = new nxFrameCAN();
TestFrame.Identifier = 357827584 + 0x20000000;
TestFrame.PayloadLength = 2;
TestFrame.Payload0 = 0xFF;
TestFrame.Payload1 = 0x0F;

//フレーム送信

status = nxWriteFrame(SessionRef, TestFrame, 24, 0.0);

 

セッション作成時にModeを11(FrameOutSinglePoint)としています

このModeではnxWriteFrameのTimeoutを0.0にするみたいです

 

これを実行してみると信号が出てきますが、データベースでTransmitTime = 0.1と

しているのにもかかわらず2ms周期で出力されてる感じです

現在CANバスに終端抵抗しか接続されていないのですが、これがまずいのでしょうか

CANの知識が乏しくここで止まっている状態です

ヒントとなる助言や文献を頂けると幸いです

 

※nxFrameCANは以下定義としました

[StructLayout(LayoutKind.Explicit, Size = 24)]
public struct nxFrameCAN        // size of structure is 24 bytes(8,4,1,1,1,1,8)
{
            [FieldOffset(0)]
            public ulong Timestamp;        //nxTimestamp_t(=u64)
            [FieldOffset(8)]
            public uint Identifier;        //u32
            [FieldOffset(12)]
            public byte Type;            //u8
            [FieldOffset(13)]
            public byte Flags;            //u8
            [FieldOffset(14)]
            public byte Info;            //u8
            [FieldOffset(15)]
            public byte PayloadLength;    //u8
            [FieldOffset(16)]
            public byte Payload0;        //u8
            [FieldOffset(17)]
            public byte Payload1;        //u8
            [FieldOffset(18)]
            public byte Payload2;        //u8
            [FieldOffset(19)]
            public byte Payload3;        //u8
            [FieldOffset(20)]
            public byte Payload4;        //u8
            [FieldOffset(21)]
            public byte Payload5;        //u8
            [FieldOffset(22)]
            public byte Payload6;        //u8
            [FieldOffset(23)]
            public byte Payload7;        //u8
}

0 件の賞賛
メッセージ7/10
6,778件の閲覧回数

済みません、間違いがありました

プログラム冒頭に宣言が不足していました

static uint SessionRef;

セッションを作成する関数の引数に誤植がありました

status = nxCreateSession(DBName, CluName, Array, IFName, 11, out SessionRef);

 

あと、Transmit Timeについてですが、気になるプロパティを見つけたので後で試して

みたいと思います

Session:Interface:CAN:Single Shot Transmit? (0x02100024)

 

0 件の賞賛
メッセージ8/10
6,771件の閲覧回数

Session:Interface:CAN:Single Shot Transmit?(0x02100024)確認しました

説明書にもあるとおり、このプロパティがFalseだと送信失敗時に再送信を試みるという

ことでした(ISO 11898-1, 6.11 Automatic Retransmission)

Trueに設定することで予定通りのフレーム送信ができました

 

やっぱりCANを使うとCANデータベースが便利そうです

メモリデータベースをこつこつ構築するのもなかなか骨が折れます

次はCAN DBファイルの利用を研究しました

手元のCAN DBファイルはNI-XNETの*.ncdではなく、あっちの*.dbcですが、何の

支障もなく使用できました

呼び出し関数は次です

[DllImport("Nixnet.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int nxdbAddAlias(
                                        string DatabaseAlias,        //const char*
                                        string DatabaseFilepath,    //const char*
                                        uint DefaultBaudRate        //u32
                                        );

使い方こんな感じで良さそうです

※Frame1、Frame2、Frame3はCAN DBファイルに記述されたフレーム名

※CAN DBファイル(CAN_SYSTEM.dbc)は実行ファイルと同じディレクトリにあるとします

static uint SessionRef;

int status = 0;

string IFName = "CAN1";
string DBAlias = "CAN_DB";
string DBPath = "CAN_SYSTEM.dbc";
uint BaudRate = 500000;

//エイリアス作成
status = nxdbAddAlias(DBAlias, DBPath, BaudRate);

//セッション作成(Frame Output Single-Point Mode)
status = nxCreateSession(DBAlias, "Cluster", "Frame1,Frame2,Frame3", IFName, 11, out SessionRef);

//送信フレーム作成 ※拡張ID
nxFrames TXFrame = new nxFrames();
TXFrame.F1_Identifier = 357826816 | 0x20000000;
TXFrame.F1_PayloadLength = 2;
TXFrame.F1_Payload0 = 0x01;
TXFrame.F1_Payload1 = 0xFF;

TXFrame.F2_Identifier = 357826848 | 0x20000000;
TXFrame.F2_PayloadLength = 3;

TXFrame.F2_Payload0 = 0x0A;

TXFrame.F2_Payload1 = 0x10;

TXFrame.F2_Payload2 = 0x00;

TXFrame.F3_Identifier = 357826880 | 0x20000000;
TXFrame.F3_PayloadLength = 1;

TXFrame.F3_Payload0 = 0xF0;

//フレーム送信
status = nxWriteFrame(SessionRef, TXFrame, 72, 0.0);

 

これでCAN DBファイルに記述されたフレームがバスに出てきました

3つのフレームが立て続けに出てくる訳ですが、各フレームのTransmit Timeが違う

場合やTiming Typeが異なる場合にどうなるのかはよくわかりません

セッションは同じ振る舞いのフレームやシグナルをまとめるものの様な気がしますが

詳細は不明です

上記内容への指摘や情報を提供頂けますと幸いです

 

※nxFramesの定義は以下としています

[StructLayout(LayoutKind.Explicit, Size = 72)]

public struct nxFrames

{

            [FieldOffset(0)]
            public ulong F1_Timestamp;    //nxTimestamp_t(=u64)
            [FieldOffset(8)]
            public uint F1_Identifier;    //u32
            [FieldOffset(12)]
            public byte F1_Type;            //u8
            [FieldOffset(13)]
            public byte F1_Flags;            //u8
            [FieldOffset(14)]
            public byte F1_Info;            //u8
            [FieldOffset(15)]
            public byte F1_PayloadLength;    //u8
            [FieldOffset(16)]
            public byte F1_Payload0;        //u8
            [FieldOffset(17)]
            public byte F1_Payload1;        //u8
            [FieldOffset(18)]
            public byte F1_Payload2;        //u8
            [FieldOffset(19)]
            public byte F1_Payload3;        //u8
            [FieldOffset(20)]
            public byte F1_Payload4;        //u8
            [FieldOffset(21)]
            public byte F1_Payload5;        //u8
            [FieldOffset(22)]
            public byte F1_Payload6;        //u8
            [FieldOffset(23)]
            public byte F1_Payload7;        //u8
            [FieldOffset(24)]
            public ulong F2_Timestamp;    //nxTimestamp_t(=u64)
            [FieldOffset(32)]
            public uint F2_Identifier;    //u32
            [FieldOffset(36)]
            public byte F2_Type;            //u8
            [FieldOffset(37)]
            public byte F2_Flags;            //u8
            [FieldOffset(38)]
            public byte F2_Info;            //u8
            [FieldOffset(39)]
            public byte F2_PayloadLength;    //u8
            [FieldOffset(40)]
            public byte F2_Payload0;        //u8
            [FieldOffset(41)]
            public byte F2_Payload1;        //u8
            [FieldOffset(42)]
            public byte F2_Payload2;        //u8
            [FieldOffset(43)]
            public byte F2_Payload3;        //u8
            [FieldOffset(44)]
            public byte F2_Payload4;        //u8
            [FieldOffset(45)]
            public byte F2_Payload5;        //u8
            [FieldOffset(46)]
            public byte F2_Payload6;        //u8
            [FieldOffset(47)]
            public byte F2_Payload7;        //u8
            [FieldOffset(48)]
            public ulong F3_Timestamp;    //nxTimestamp_t(=u64)
            [FieldOffset(56)]
            public uint F3_Identifier;    //u32
            [FieldOffset(60)]
            public byte F3_Type;            //u8
            [FieldOffset(61)]
            public byte F3_Flags;        //u8
            [FieldOffset(62)]
            public byte F3_Info;            //u8
            [FieldOffset(63)]
            public byte F3_PayloadLength;    //u8
            [FieldOffset(64)]
            public byte F3_Payload0;        //u8
            [FieldOffset(65)]
            public byte F3_Payload1;        //u8
            [FieldOffset(66)]
            public byte F3_Payload2;        //u8
            [FieldOffset(67)]
            public byte F3_Payload3;        //u8
            [FieldOffset(68)]
            public byte F3_Payload4;        //u8
            [FieldOffset(69)]
            public byte F3_Payload5;        //u8
            [FieldOffset(70)]
            public byte F3_Payload6;        //u8
            [FieldOffset(71)]
            public byte F3_Payload7;        //u8

}

0 件の賞賛
メッセージ9/10
6,669件の閲覧回数

CANフレーム送信について研究してきましたが、ここでCANフレーム受信を試みたいと思います

CANバスに流れたデータフレームの読み取りをします

呼び出し関数は次を使いました

[DllImport("Nixnet.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int nxReadFrame(
                                        uint SessionRef,        //nxSessionRef_t
                                        out nxFrameCAN Buffer,        //void*
                                        uint SizeOfBuffer,        //u32
                                        double Timeout,            //f64
                                        out uint NumberOfBytesReturned    //u32*
                                        );

以下の使い方でフレームの取得ができました

※RXFrameはCAN DBファイルに記述されたフレーム名

※CAN DBファイル(CAN_SYSTEM.dbc)は実行ファイルと同じディレクトリにあるとします

※nxFrameCANは1フレーム分の構造体

static uint SessionRef;

int status = 0;
uint Datasize = 0;

string IFName = "CAN1";
string DBAlias = "CAN_DB";
string DBPath = "CAN_SYSTEM.dbc";
uint BaudRate = 500000;

nxFrameCAN RXData = new nxFrameCAN();

//エイリアス作成
status = nxdbAddAlias(DBAlias, DBPath, BaudRate);

//セッション作成(Frame Input Single-Point Mode)

status = nxCreateSession(DBAlias, "Cluster", "RXFrame", IFName, 8, out SessionRef);

//フレーム受信

status = nxReadFrame(SessionRef, out RXData, 24, 0.0, out Datasize);

 

これでフレームがRXDataに入ります

あとはnxFrameCANの定義に基づいてペイロードや時間を取り出すことになります

Frame Input Single-Point Modeでは最新のフレーム情報を取得するためにTime Outは0にするみたいです

なお、上記だと1フレームの取得にしか使えません

複数フレームを取得する場合にはデータを受け取るvoid* Bufferに工夫が必要です

前回使った3フレーム用のnxFramesとかバイト配列などを活用することになる気がします

関数からはデータのバイト数が返ってくるので可変長データとかにも対応できると思われます

上記の実行結果では24バイト(=1フレーム)が返ってきました

 

上記内容への指摘や情報を提供頂けますと幸いです

0 件の賞賛
メッセージ10/10
6,505件の閲覧回数