スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ArduinoでCAN通信(その6:CCP通信準備)

前回の5回までで、J1939プロトコルを模したCANの送受信ができたが、今度はもう少し技術的ハードルを上げ、「CCP通信」にトライしてみようと思う。CCP通信の記事を見ると、「CCPとは“CAN Calibration Protocol”の略であり、CANを使った(ECUへの)測定/キャリブレーションプロトコルという意味になります」とあります。

またそれらの記事を読んでみると、
「 CCPは、CANの普及とともに広く使用されるようになりました。その背景は、ECU内部のソフトウェアへアクセスするための通信媒体としてCANを使 うことで、低コストで汎用性のある測定/キャリブレーションが実現できたからです。実際、車載ネットワークに接続されているECUにCCPドライバを組み 込むだけで、すでに設置されているCANを使ったECUの測定/キャリブレーションが可能となります。」

ということでかなり汎用的に使われている通信仕様となります。
現在ではCCPは上位機能のあるXCP規格に統合されたようですが、詳しくはNETなどの解説に任せます。

「ECU内部へアクセス」 ということで、(ECUへ)CAN送信し、(ECUから)CAN受信することになります。
即ち、実際のECUとのCCP送受信に関する勉強になります。

【最終的に作ろうと思っているArduino-CAN通信器の回路】

Arduino_CAN_26_sch_3.jpg

いきなり回路図を出されても何のこっちゃ!ですよね。

【これのEagle基板図】

Arduino_CAN_27_brd_3.jpg

これもまあごちゃごちゃしていて判りませんが、

・Atmega328Pマイコンを搭載(真ん中の32ピン)
・MCP2515 CANコントローラ
・MCP2551 CANトランシーバ
・microSDカード装置を積む
・BlueTooth ドングルを載せる
・LCD(デバックを主体にした用途)
・電源やスイッチ、インタフェース等の回路

これらを 90mm x 55mm程度の小さな基板上に配置した、「Arduino版 CAN 通信ユニット」です。
Arduino UNOと同じピッチのコネクタ配置にしてあるのは、上に基板を追加すれば、Analog出力や入力、はたまた他の機能を増設できることを考えています。
とは言ってもArduinoのデジタルピンの割り付けはすでに満杯状態なので、機能切り替え目的にDIPスイッチに割り付けているピンを流用できるようにしたほうが良いでしょうね。MCP4922 という2chの12Bit DA変換ICならば、microSDに使っているSPI信号を使ってアナログ出力を作ることが可能です。SPI接続機器を使い分けるCS信号だけがあれば良いと思われます。

表面実装のAtmega328 を使用するので、ブートローダ書き込み用ICSP端子、スケッチ書き込み用の(DTR,RX,TX,GND)端子を設けています。USB接続のFT232の入出力をここに取り付けて、書き込みやシリアルモニタに使用します。

microSDカードを装備したのは、ECUにアクセスするための設定をここに書き込んでおき、Arduinoに読み込ませる目的です。
ドライブにFat FSを用いる予定なので、Atmega168ではプログラム・エリアが足りません。

・・・とは言っても、最初からこの基板を作ってチャレンジするほど自信は無いので、実際のトライに使うのは、先に作った Arduino用 2階建て基板とArduinoもどき です。
今回新採用となるmicroSD部分をユニバーサル基板に組んで、Arduino にピン接続しても良いのですが、センシティブなSPI信号によるアクセスなので(以前、STM32で懲り懲りした経験あり)、新たにCAN計測基板を手つくりします。

【回路図】
Arduino_CAN_28_sch.jpg

追加したmicroSDカード(秋月製 AE-MICRO-SD-DIR)を差し込む端子が左上です。
SPI接続に4ピン、電源とGNDに2ピンの6ピンに配線します。

接続する端子名に、SD_***が付いていますが、Arduino のピン 10,11,12,13 は本来の Arduino SPI 端子です。
CAN コントローラのSCK 端子にピン 12を割り当てていたので、これをピン 9に移動しました。CAN コントローラを制御するソフトが、ソフトウエア的動作するSPIの仕組みを作ってあるので、こんな際にとても便利です。

また、microSDカードのピン下に680と360Ωの抵抗が付いていますが、これはArduino から出てくる 5V信号を抵抗で 3.3Vに分圧させる抵抗です。1kΩ台に変更しても動作に問題は出ないでしょう。
(最終予定のArduino-CAN基板では、このインターフェイスは専用IC・TXS0108E を使う予定です)

【基板図・参考まで】
Arduino_CAN_29_brd.jpg

さてハードウエアの事前準備は整いました。設計したmicroSD搭載基板を作ればOKです。

次はCCP通信手順の構築ですが、これはCMでの情報を使うことにします。

・・・昔、プロのお手伝いを頂き、Windows2000搭載Box型コンピュータとVisual・Basic、そしてUSB-CANで構築したシステムを使い、CCP通信をしたことがあります。
かなりの量のコーディングでしたが、果たして、小生の貧弱なC言語スキルで Arduino を使いこなせるものでしょうか?






ArduinoでCAN通信(その5:送信)

前にも記載しましたが、中華製 J1939 送信エミュレータは 通信が最速0.1秒間隔なので、実機が最速 20msec 間隔で送ってくるデータ処理ができるかどうかの判断には使えない。

【J1939 送信エミュレータ】
Arduino_CAN_2.jpg

そこで Arduino + MCP2551 + MCP2515 による2台のノードで、送受信させようというわけです。

・・・2日程度かけてやっと送信ができました。

【送受信中の2台のCAN機器】
Arduino_CAN_23.jpg

左が送信機として動作している Atmega328P マイコン搭載機、右が受信中の Atmega168P搭載機です。
Atmega328Pの在庫が切れたので 168 を使ったが、コンパイル後のソースが9kByte弱なのでOKです。
受信機の赤いLEDが点灯していますが、上側がRX受信端子、下側がINT受信割り込み端子に接続してます。

MCP2515には送信バッファが3個用意されていますが、今回のテストでは1個しか使ってません。
データ転送は 0.1秒(100msec)間隔で同時6個送信するプログラムですが、実際には1データ 20~30msec 間隔ぐらいが最速のようです。

後に記載するプログラムでは、送信バッファにガンガン送信しても、送信できない場合はデータを喪失してしまうので、コントロールレジスタのフラグをチェックしながら転送制御する必要があります(今回はまだ未実装です)

【正常に受信できている場合のシリアル・モニタ】   注:拡張idの一部を非表示にしています
Arduino_CAN_25.jpg

モニタ初期はid末尾3桁が300,400,111の3個だけですが、途中から100も現れます。
送信は6個のデータ・セットを100msec間隔で送出(個々のデータでは計算上 17msec 間隔)するようにしていますが、これはとても無理のようで、実質は 20~30msec間隔が最速のようです(送信バッファ1個での条件です)

実機での最速インターバルが 20msec ということなのでまずまずかなと思います。
実際の使用環境では、受信したデータはデータエリアに記録して置き、一定時間間隔(100msec程度)でそれを参照して使うので、受信が少々遅れても大きな問題にはなりません。(変化量が比較的少ないので、大きな誤差に至らないという意味です)

尚、6種のデータを送ってますが、4種のidだけが受信されるように、受信側でフィルタを設定しています。

【送信プログラム】

受信に使ったプログラムをベースに、メインプログラムの不要な部分を削除し、送信部を追加しました。

1.不変部分
 ・CAN初期化
 ・SPI設定
 ・タイマ設定
 ・LCD設定(実際にはLCDは使っていない)
 ・他

2.削除部分
 ・フィルタ・マスク
 ・受信割り込み

3.追加部分
 ・送信データ(拡張id、8Byteデータ、各々6個)
 ・送信バッファのIDレジスタ用データ、4種×各6個、処理速度を上げるために用意しておく
 ・送信手順

追加した部分のプログラム・コード(***部分は非表示です)

// --------------
// N258_TX.ino
// ---------------
// 拡張id
long id[7] = { 0x0000000,0x18F**400,0x18F**300, 0x18F**111,0x1CF**100,0x18F**200,0x18F**E00 };  //、0番は無効ID 
byte dat[7][8] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},  //7個並べる、0番は無効 
                          {0xFF,0xFF,0xFF,0x3E,0x78,0xFF,0xFF,0xFF},
                          {0xFF,0xAC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
                          {0xFF,0x63,0x00,0xFF,0xFF,0xFF,0xFF,0xFF},
                          {0xFF,0x0D,0x05,0xFF,0xFF,0xFF,0xFF,0xFF},
                          {0x00,0x78,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
                          {0x7B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}  };
//SID,EID 変数エリア
byte SIDH[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};
byte SIDL[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};
byte EID8[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};
byte EID0[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};

// ・・・

void make_id( byte num, long id);  //送信バッファを事前に作る処理宣言
void alldataTx();  // 全6データを送信する処理宣言

// ・・・

// 事前にidを作っておく処理の実体
// SIDH,SIDL,EID8,EID0 を複数作るときの処理
void make_id( byte num, long id) {
  word canid;

  canid = (word)(id & 0x0FFFF);
  EID0[num] = (byte)(canid & 0xFF);
  EID8[num] = (byte)(canid >> 8);
  canid = (word)(id >> 16);
  SIDL[num] = (byte)(canid & 0x03);
  SIDL[num] += (byte)((canid & 0x1C) << 3);
  SIDL[num] |= 0x08;
  SIDH[num] = (byte)(canid >> 5);
}

// ・・・

//全データを送信する処理実体
void alldataTx(){
  struct tag_TXBnCTRL *pctrl;
  struct tag_TXBnDLC *pdlc;
  byte i, j, k, reg, p;
  word msgL,msgH;
  long msg2;
  byte ctrl, dlc;
 
  for(i = 1; i < 7; i++){
    MsgBuf[1] = SIDH[i];
    MsgBuf[2] = SIDL[i];
    MsgBuf[3] = EID8[i];
    MsgBuf[4] = EID0[i];
    MsgBuf[5] = 0x08;
    for(j = 6; j < 14; j++){
      MsgBuf[j] = dat[i][j-6];
    }
   
    CANTxB0Write(&MsgBuf[1],8);   //送信バッファシーケンシャルライト
    /*
    ctrl = 0;
    pctrl = &ctrl;
    pctrl -> TXP = 0;
    pctrl -> TXREQ = 1;
    */
    //ctrl = 0;
    ctrl = 0x08;
    //CANWriteReg(TXB0CTRL, ctrl);     // これは参考書にあった記述だが不要だった(動かない)
   
    CANRTS();  // あるページで見つけた上の行に対する代替え機能
    delay(10);
}

ここで注釈します。参考書では、CANWriteReg(TXB0CTRL, ctrl); と記述すれば、MCP2515のコントロールビットがONになり送信が開始されると書かれている。・・・しかし動かない。
データシートを見ると同様のことが書かれているが、その後をよく読むとRTSで起動させることが必要とある。

その方法は、
1.SPIによる書き込みコマンドによってレジスタに書き込み、
2.SPI RTS コマンドを送る
3.RTSピンをLOWにするハード的な起動、

この記述で最も不明だったのが、これらは and なのか or なのか、はたまたそれらの組み合わせなのかが判らない。
で、あるページで成功したという記述を見つけました。

それは、2.です。

これに準じてコードを生成すると動きました(最初しばらくは動作不安定でしたが・・・)

以下を追記します
// ------------------
// CAN2515.h
// ------------------
// --------------------------------------------------------------------
// CAN送信
// --------------------------------------------------------------------
void CANRTS(void) {
  byte i;
  byte cmd = SPI_INST_RTS;
  cmd |= _BV(0);

  CAN_SPI_CS_L;

  for(i = 0; i < 8; i++) {
    if(cmd & 0x80) {
      // 1
      CAN_SPI_SO_H;
    } else {
      // 0
      CAN_SPI_SO_L;
    }
    cmd <<= 1;
    CAN_SPI_SCK_H;    // SCK パルス出力
    CAN_SPI_SCK_L;
  }

  CAN_SPI_CS_H;
}

/////
はい、以上で J1939 CAN のデータ送信・受信までのプロセスを習得できました。

これらのスキルを活かし、次のステップは、応用豊か&実用できるCANツールの構築です。

以下はご要望あれば送付します。エントリーください。
// --------------------
// プログラム・ファイル
// --------------------

Arduino 送信プログラム
P258_TX.zip

Arduino受信プログラム
P258_N2.zip






ArduinoでCAN通信(その4:割り込み受信)

当初の計画は、

【今後のお勉強課題】
1.ID識別で、続くデータを処理する機能のテスト(取りあえずシリアル通信を受けたPCで処理)
2.この機能をArduinoマイコンに実装
3.Arduinoマイコン(実体はMCP2515)でCAN IDによるフィルタ機能の実装(実機で使えるように)
4.BlueToothによる通信と(当初はWindowsPCへ)、Androidタブレットまたはスマホでの表示

なので、現在は 4.の半分まで到達したのだが、なにせ、Androidソフト開発は敷居が高い。

良く考えてみると、現在のCAN受信はメインプログラムのポーリング・チェックで行う方式なので、受信応答に不安がある。
今回のテスト環境は0.1sec間隔程度の受信なので問題が発生していないが、実機では50mSecや20mSec間隔で通信されていると情報があるので、「現在のポーリング受信」から、「受信割り込み処理」に変更する必要がある。

【変更する部分】
・MCP2515 CANコントローラに受信割り込みが発生するとINTピンがLOWになるので、
 これをArduinoの割り込みPIN2に接続する。
 PIN2に接続されていた、SCKはPIN12に移動する。
 これに伴い、SPI接続定義を変更する
・また、現在のJ1939送信シミュレータでは0.1秒間隔の送信しかできないようなので、送信のためのノードも作る必要が生ずる。すなわち、送信プログラムも作る必要がある。

【新規に作ったノード】
Arduino_CAN_19.jpg

右下が新規に作ったノードです。機能切り替えに使うスイッチなども付けるので一回り大きいです。
赤い模様状の左に見える赤いLEDが、CAN_RXと割り込みINT に接続され、かすかに光って動作中です。

【回路図】
Arduino_CAN_17_sch_2.jpg
 
・MCP2515の12ピン INTを Arduinoの割り込み入力0 になる2ピンに接続する。
 この Arduino 2ピンに接続していた SPI のクロック SCK は Arduino 12ピンに移動した。今使っているCANプログラムは Arduinoの SPI.hを使わずに 直接ピンアサインできるので便利です。
・ソフトの機能切り替えに使うと思われるDIPスイッチを追加してます(機能はまだ実装してません)

// ----- 変更するソース ---- (赤字で書いた部分が要注意点です。最初勘違いで少しはまりました)
// P2515SpiPort.h
//
//    MCP2515 接続ポートの定義(for Arduino #258)
//        09/04/06

#ifndef SPIPORT_H
#define SPIPORT_H

// SPIポート定義
#define CAN_SPI_CS_BIT       5        //
#define CAN_SPI_SO_BIT       3        //
#define CAN_SPI_SCK_BIT    4       // 2   (注:ArduinoのPIN12ではなく、ATMEGA328の、RB4ピンを指す)
#define CAN_SPI_SI_BIT        4        //

#define CAN_SPI_CS_DDR       DDRD        //
#define CAN_SPI_SO_DDR       DDRD        //
#define CAN_SPI_SCK_DDR      DDRB    //DDRD   (注:DポートからBポートに変更)
#define CAN_SPI_SI_DDR         DDRD        //

#define CAN_SPI_CS_PORT       PORTD        //
#define CAN_SPI_SO_PORT       PORTD       //
#define CAN_SPI_SCK_PORT    PORTB     //PORTD   (注:PORTDからAtmega328のPORTBに変更)
#define CAN_SPI_SI_PORT         PIND           //

#endif
// ------------------------

Atmega328 と Arduino のピン割り付け図はこちらのページを参照ください。

//-------------------------
// P258_N1をベースに、P258_N2 に変更したソース
// タイムカウントのタイマーを 100msec から 10msec に変更
    //OCR1A = 25000;                // コンペア値
  OCR1A = 2500;        // コンペア値
// ------------------------

// ------------------------
// P258_N2.ino

//  setup部に以下を追加

  // 受信割り込みをセットする
  CANWriteReg(CANINTE, 0x03);

//   Pin2  <- MCP2515 INT による割り込み宣言
//   Pin2 が割り込み0、立ち下がった時に、MCP2515_ISR を実行する
   attachInterrupt(0,MCP2515_ISR,FALLING);  // MCP2515 INT Pin LOW -> Pin2 interrupt
// ------------------------

【参考:MCP2515の受信割り込み許可レジスタ】
Arduino_CAN_22.jpg  

// ------------------------
// P258_N2.ino
// 割り込み処理では、flgRecv を1に変更するだけ
void MCP2515_ISR(){
    flgRecv = 1;
}
// -------------------------

// ------------------------
// P258_N2.ino
    //if(get_num != 0) {      // これを止め

  // データ受信割り込み
  if(flgRecv){                 // flgRecv をチェックする
    //
    flgRecv = 0;             // フラグを戻す
       
    // ステータス・チェック
    reg = CANReadStat();
    if(reg & 1<<CANINTF_RX1IF) {
      // 受信あり ロールオーバ発生中
      MsgBuf[0] = CANReadReg(RXB1CTRL);

      // RXB1からメッセージ、データの読み出し
      CANRxB1Read(&MsgBuf[1], 8);
     
    } else if(reg & 1<<CANINTF_RX0IF) {
      // 受信あり
      MsgBuf[0] = CANReadReg(RXB0CTRL);

      // RXB0からメッセージ、データの読み出し
      CANRxB0Read(&MsgBuf[1], 8);
     
    }
   
    // 受信割り込みフラグをリセットする
    CANWriteReg(CANINTF, 0x00);
    // 注:本来ならば0,1ビットだけを 0 リセットすべき
   
    // 受信あり
    if((MsgBuf[2] & 0x08) != 0x08) {
    // 以下省略
// ------------------------------

【参考:MCP2515の割り込みフラグレジスタ】
Arduino_CAN_21.jpg
割り込み終了後にこれをリセットしないと、再度割り込みができません。


【割り込み受信に変更した結果】
Arduino_CAN_20.jpg

Arduinoのシリアルモニタの結果を示します。
ID 18F00400 は約0.1sec間隔で流れてきていますが、そこに1秒に1回の頻度で、ID 18F00200 と 18FEEE00が割り込んで来るというように見える。この2つのIDは全く同時刻になっているので、RB0,RB1で受信処理されたのかもしれない(発送元がどのような時間で送信しているか良く判らない)
次に受信した18F00400 が、これら2つのデータの0.02秒後になっているので、新たに追加した割り込み受信の仕組みは速い受信性能が期待できる。
INT端子に接続されたLEDの光り方を見ても、薄く点滅している中で約1秒周期で明るく光るので、そこに上記の3信号が集中して受信していると見てよさそうである。

さて次は、「実機と同じように速いサイクルで送信できるノードの製作」 なのですが、私が参考にした本の記事には、送信があまり載っていません。
同じ筆者によると思われる、エレキジャックの記事を参照頂ければわかりますが、この程度です
(このページに割り込み受信に関する記述が載っています。これをヒントにしました)

実は、seeedのサンプルプログラムには送信例が載っていますが、ベースとなる部分が難解なので私のCANの勉強には敷居が高そうです。

うむむ、これは送信プログラムを作るしか無さそうです。





ArduinoでCAN通信(その3:BlueTooth送信)

さて次は BlueTooth による送信ですが、Windows PCでは、あっという間にできてしまいました。

【BlueToothで送信中の本器】
Arduino_CAN_13.jpg

BlueToothであるUSBドングルの青LEDと同基板上のチップ赤LEDが点灯(実際は点滅)しています。
搭載しているBlueTooth は Class1 仕様という 100m の通信ができるものです。

余談になりますが、CMで使う機器で通信距離を試したことがありますが、見通しがきく環境ならば100mはOKでした。
BlueTooth なかなかのものです。

【BlueToothのCOM4での受信状態】
Arduino_CAN_14.jpg

ArduinoのUART通信レートが19200なのでBlueToothでも同じです。問題無くPC側で受信できてます。
RPMは約0.1秒間隔で、VSS(車速)、ECT(温度)は約1秒間隔です。

PC側の設定
Arduino_CAN_15.jpg

Arduino_CAN_16.jpg

COM4がSPPになっているのでこのポートを使います。

(注意:これは受信側PCがWindows10での例です。OSによっては設定が異なり、第3のポートが通信用SPPポートになることがあるようです)

いやいや、こんなに簡単にWindows PCへの BlueTooth 送信ができてしまいました。

さてさて、この先のタブレットやスマホでの受信はどうなるのでしょうか。
Android対応ソフトの開発環境・Android Studio をインストールしてありますが、一度しか動かしたことありません。
特に、JAVAを使ったことが全くないので、お先が見通せませんね。

なんて、いまから弱音を吐いてます。




ArduinoでCAN通信(その2:拡張IDでフィルタ、マスク)

「ArduinoでCAN通信」の次は、拡張IDを使いCANコントローラMCP2515でハード的にフィルタ、マスクを行う機能を追加することになります。

オリジナルのソースには、フィルタ、マスクに関して次のような記述があります。
//-------
// P258_N1.ino
//
    // フィルタ、マスク設定 SID 11bit
    CANSetSidFilter0(0x0000);
    CANSetSidFilter1(0x0000);
    CANSetSidFilter2(0x0000);
    CANSetSidFilter3(0x0000);
    CANSetSidFilter4(0x0000);
    CANSetSidFilter5(0x0000);
    CANSetSidMask0(0x0000);
    CANSetSidMask1(0x0000);

    CANSetFilterRxB0(3);            // RXB0でフィルタ、マスクは使用しない
    CANSetFilterRxB1(3);            // RXB1でフィルタ、マスクは使用しない
//-------------

これの意味を調べてみると
・これは標準ID用です。(拡張ID用は作らねばならない)
・フィルターは0から5まで6個設定ができる
・マスク(スルーさせる部分)は2個設定ができる
・フィルタ、マスク共、0x0000 という、0を設定するとフィルタしない、マスクしないという意味
・フィルタ、マスクをRxB0、RxB1で使う:0、標準IDとして使う:1、拡張IDとして使う:2、使わない:3

さらに、これらのプログラム構成を調べると、
// ------
// CAN2515.h 内にマクロが記述されています
//
// フィルタSID設定 (注:0だけ表示、5まであります)
#define CANSetSidFilter0(sid) CANSetSidFilteMask(RXF0SIDH, sid)
//
// マスクSID設定
#define CANSetSidMask0(sid) CANSetSidFilteMask(RXM0SIDH, sid)
#define CANSetSidMask1(sid) CANSetSidFilteMask(RXM1SIDH, sid)
// ------

そしてマクロを実行するプログラム本体は
// ------
// --------------------------------------------------------------------
// CAN SID フィルタ、マスク設定 (兼用ルーチン)
// --------------------------------------------------------------------
// CAN2515.h 内
//
void CANSetSidFilteMask(byte adrs, word sid) {
    CANWriteReg(adrs, (byte)(sid>>3));            // HIGH
    CANWriteReg(adrs + 1, (byte)((sid & 0x07)<<5));        // LOW
}

CANWriteREG() というのはSPIを使ってMCP2515指定アドレスにByteデータを書き込む部分ですので、フィルタ、マスクを書き込むルーチンを拡張ID用で作れば良いということのようです。

// --------
// CAN2515.h に追加した記述
//
// フィルタEID設定
#define CANSetEidFilter0(eid) CANSetEidFilteMask(RXF0SIDH, eid)
#define CANSetEidFilter1(eid) CANSetEidFilteMask(RXF1SIDH, eid)
#define CANSetEidFilter2(eid) CANSetEidFilteMask(RXF2SIDH, eid)
#define CANSetEidFilter3(eid) CANSetEidFilteMask(RXF3SIDH, eid)
#define CANSetEidFilter4(eid) CANSetEidFilteMask(RXF4SIDH, eid)
#define CANSetEidFilter5(eid) CANSetEidFilteMask(RXF5SIDH, eid)
//
// マスクEID設定
#define CANSetEidMask0(eid) CANSetEidFilteMask(RXM0SIDH, eid)
#define CANSetEidMask1(eid) CANSetEidFilteMask(RXM1SIDH, eid)
//
// -------------------
// プロトタイプ
void CANSetEidFilteMask(byte adrs, long eid);
// -------------------------------------------------------------------- 
// CAN EID フィルタ、マスク設定(兼用ルーチン)
// --------------------------------------------------------------------
void CANSetEidFilteMask(byte adrs, long eid) { 
  word canid;                                                   // 抽出2バイト値
  byte bSIDH;                                                   // フィルタ標準ID上位
  byte bSIDL;                                                   // フィルタ標準ID下位
  byte bEID8;                                                   // フィルタ拡張ID上位
  byte bEID0;                                                   // フィルタ拡張ID下位

  canid = (word)(eid & 0x0FFFF);       // 拡張IDの下位側2バイトをcanidに抽出する
 
  bEID0 = (byte)(canid & 0xFF);          // canid の下位1バイトがフィルタ拡張ID下位
  bEID8 = (byte)(canid >> 8);              //  同上の上位1バイトがフィルタ拡張ID上位
  canid = (word)(eid >> 16);                // 拡張IDから上位2バイト分の標準ID部分を
                                                   // canidに抽出する
  bSIDL = (byte)(canid & 0x03);          // フィルタ標準ID下位に抽出したcanid の
                                                   // 下位2ビット(EID17,EID16)を代入する
  bSIDL += (byte)((canid & 0x1C) << 3 );    // 同上のb4,b3,b2の3ビットを3ビット
                                                   // 上方シフトして加える(SID2,1,0)
  bSIDL |= 0x08;                                 // 拡張IDを示すEXIDEビット(b3)を立てる
  bSIDH = (byte)(canid >> 5);              // フィルタ拡張ID上位は 抽出していた値を
                                                   // 5ビット右シフトして代入する
  CANWriteReg(adrs, bSIDH);                     // MCP2515 には、SIDH,SIDL,EID8,EID0 の
  CANWriteReg(adrs + 1, bSIDL);               // 順にアドレスが並んでいるので
  CANWriteReg(adrs + 2, bEID8);               // 順に書き込む
  CANWriteReg(adrs + 3, bEID0);
}
// ------------------

これを作るに当たり、先に参考にしたseeedのソースをまた参考にしました。
元となったソースでは、標準IDか拡張IDかを判定するフラグによって処理を分岐させるように作ってあります。汎用的に使われる機器の場合はそのような作りが好ましいと思えます。

忘備録として、フィルタIDバッファと拡張IDデータの関係を図で記載します。
表内のSIDは標準ID、EIDは拡張ID用のビットです。

RXFnSIDH フィルタn標準ID 上位(n=0,1,2,3,4,5)
(アドレス:0x00,0x04,0x08,0x10,0x14,0x18)
b7b6b5b4b3b2b1b0
SID10SID9SID8SID7SID6SID5SID4SID3

RXFnSIDL フィルタn標準ID 下位(n=0,1,2,3,4,5)
(アドレス:0x01,0x05,0x09,0x11,0x15,0x19)
b7b6b5b4b3b2b1b0
SID2SID1SID0-EXIDE-EID17EID16
EXIDE は拡張ID許可ビットで、0:標準ID、1:拡張ID を示します。

RXFnEIDH フィルタn拡張ID 上位(n=0,1,2,3,4,5)
(アドレス:0x02,0x06,0x0A,0x12,0x16,0x1A)
b7b6b5b4b3b2b1b0
EID15EID14EID13EID12EID11EID10EID9EID8

RXFnEIDL フィルタn拡張ID 下位(n=0,1,2,3,4,5)
(アドレス:0x03,0x07,0x0B,0x13,0x17,0x1B)
b7b6b5b4b3b2b1b0
EID7EID6EID5EID4EID3EID2EID1EID0


このフィルタバッファに拡張IDデータを入れるわけだが
拡張IDには、連なる4バイトにデータが下図のように並んでいる。

b7b6b5b4b3b2b1b0
---SID10SID9SID8SID7SID6

b7b6b5b4b3b2b1b0
SID5SID4SID3SID2SID1SID0EID17EID16

b7b6b5b4b3b2b1b0
EID15EID14EID13EID12EID11EID10EID9EID8

b7b6b5b4b3b2b1b0
EID7EID6EID5EID4EID3EID2EID1EID0

このような配置なので、先のようなCAN EID フィルタ、マスク設定ルーチンになる。


次にメインのunoソースを変えます
// -----------
// N258_N1.uno
//
// SID用フィリタ、マスク部をコメントアウト(省略)
//
  // フィルタ、マスク設定 EID 29bit
  CANSetEidFilter0(0x18F00200);   //J1939 sim 'VSS' 6Byteが、km/hr
  CANSetEidFilter1(0x18F00400);   //J1939 sim 'RPM' 5Byteの64倍が、r/min
  CANSetEidFilter2(0x18FEEE00);   //J1939 sim 'ECT' 1Byteの-40オフセットが、℃
  CANSetEidFilter3(0x00000000);
  CANSetEidFilter4(0x00000000);
  CANSetEidFilter5(0x00000000);
  CANSetEidMask0(0x7FFFFFFF);     //全てのビットを立てるとフィルタの全一致
  CANSetEidMask1(0x7FFFFFFF);     //(同上)

  CANSetFilterRxB0(0);            // RXB0でフィルタ、マスクを使用する
  CANSetFilterRxB1(0);            // RXB1でフィルタ、マスクを使用する
// -----------

3個の拡張IDを通すようなフィルタ、マスクにしました。

フィルタを通ってきた3種のデータの後に数値を表示させてみました。

// -------------------------
// N258_N1.uno
//
    //CANデータを処理し、数値を追加
    if(msg2 == 0x18F00200){
      sprintf(StrBuf, "-VSS:%d(km/hr)", MsgBuf[11]);
      PUTSTR(StrBuf);
    }
    if(msg2 == 0x18F00400){
      sprintf(StrBuf, "-RPM:%d(r/min)", (word)(MsgBuf[10]*64));
      PUTSTR(StrBuf);
    }
    if(msg2 == 0x18FEEE00){
      sprintf(StrBuf, "-ECT:%d(deg)", (word)(MsgBuf[6]-40));
      PUTSTR(StrBuf);
    }
// ----------------------------

【拡張IDフィルタを使った処理結果、シリアルモニタ】
Arduino_CAN_12.jpg

回転数rpmは0.1秒間隔くらいで送られてきています。
これぐらいのデータ量ならばArduinoで難なく処理できているということでしょう。

さて、計画していたことは
【今後のお勉強課題】
1.ID識別で、続くデータを処理する機能のテスト(取りあえずシリアル通信を受けたPCで処理)
2.この機能をArduinoマイコンに実装
3.Arduinoマイコン(実体はMCP2515)でCAN IDによるフィルタ機能の実装(実機で使えるように)
4.BlueToothによる通信と(当初はWindowsPCへ)、Androidタブレットまたはスマホでの表示

1.2.を飛ばして3.ができてしまいました。

お次は4.の前半・BlueToothによる結果の伝送です。





プロフィール

haiga

Author:haiga
私のブログへようこそ!
電気オンチが始めた自作オーディオです
2010/3/17 電子工作をプラスしました。

自作オーディオの楽しみ共有のため、私が作ったパーツ提供をしてます。質問や要望を遠慮なくコメント欄に書き込んでください。

FC2カウンター
その日1回目にアクセスいただいた方の総カウントです
最新記事
最新コメント
最新トラックバック
カテゴリ
月別アーカイブ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。