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による結果の伝送です。





ArduinoでCAN通信(その1)

CMでJ1939規格に準拠した機器からCAN通信するという仕事があった。
業務の場合、それなりの機器を入手して簡単にCAN通信が行えるのだが、それでは自分のスキルアップにはなりにくい。

そこで業務とは別に、CAN通信の勉強ができそうなArduinoマイコンとCANトランシーバ/MCP2551、CANコントローラMCP2515の組み合わせで勉強することにしました。この組み合わせは非常に情報が多いので助かりそうです。

今回はJ1939信号をArduinoマイコン+CAN基板で計測するまでを記載します。

【準備】

CAN通信の細かなことを勉強するには書籍も必要だろうということで選んだのが、これです。

Arduino_CAN_6.jpg

かなり細かな記述がされ、Arduinoの回路図やプログラムのダウンロードが可能です。

まずはCAN受信から学ぶことになるのですが、J1939準拠の信号を出してくれるものがないと学習がうまく進まない。
色々と探してみたらありました。こちらです。 下図、右上です。

Arduino_CAN_7.jpg

いずれのエミュレータも、5~8種の可変項目を持っていそうです。(VOLの数から見て)

この4種の J1939 エミュレータから最も安価で、LCDが付いているビジュアルなものを選びました。

Arduino_CAN_2.jpg  

ところが、安いものは安いだけのことがありました。 「中華品質」に所以しているのでしょうかね(内容は後述)

さて、肝心なのはCAN受信機器です。
先の既述のように、「Arduinoマイコン、CANトランシーバ/MCP2551、CANコントローラMCP2515」 です。

Arduino_CAN_4.jpg

中央下が コントローラMCP2515、その右が CANトランシーバ/MCP2551、その上が CAN動作に連動しているLED 4個 (電源黄色、送信、受信、SPI動作チェック) です。
この写真では良く見えませんが、Rの受信LEDが点滅しています。

LCDはI2Cインタフェースで接合してます。マイコン 5V と LCD 3.3Vのインタフェース電圧変換のFET回路も付けましたが、このLCD(ストロベリーリナックス、SB1602B) は 5Vそのままでも駆動できるようです。

CAN以外の外部インタフェースは、右下に見えるUSB-COM変換ポートで、計測結果を外部PCに送ります。

しかし、最終的にやりたいことは、左中央に見える BlueTooth による出力です。
この機器を最小限に小さく作り(Arduino基板と一体化)、CAN計測とBlueToothによるデータ転送を受け持ち、転送後のデータ処理を種々行えば、様々なCAN情報収集と応用に発展できないか、というのが私の目論見です。

【回路図】
書籍の図10-3 を一部参考に、作った回路図(Eagle)です
Arduino_CAN_8_sch.jpg
CAN関係でオリジナルと異なる部分は
・MCP2551,MCP2515は表面実装SSOPタイプから、差し込み式ISPに変更
・終点抵抗回路が入っていません
・MCP2515のクロックは水晶発振20MHzから16MHzに変更(このほうが250K,500K CANに合わせやすい)
・SPI回路のプルアップ抵抗無し(動きます)
・MCP2515のRESET回路(19ピン)にはVDD(20ピン)から10kΩで渡す(リセットICを使わず)

その他の変更点は
・LEDは直接駆動で4灯のみ
・サーボやモーターなどの駆動回路は不要なので削除
・I2C駆動のLCD(SB1602B)を設置、これ用の3.3V電源、マイコン5VからLCD3.3Vへの信号レベル変換FETを追加
・BlueTooth(SBDBT5V)と、UARTをArduino書き込み時とBlueTooth使用時で切り替えるスイッチを搭載

【基板図】
Arduino_CAN_9_brd.jpg
結構一杯一杯に詰まっていますが、これはテスト機なので、うまくできた時には表面実装IC類やコンパクト端子を使えば、Arduinoマイコンを含み一枚の小さな基盤に載るだろうと思ってます。

【CAN通信テスト】

通信テストのベースにさせて頂いたソフトは、参考書籍のサポート・ページ(http://mycomputer.cqpub.co.jp)です。
このページを辿ると、CAN_Arduino.zip というフィルがダウンロードできます。

*6月1日までメンテナンスのようです。
*オリジナル・ファイル Can_Arduino_org.zip は こちらから
*私が改定したファイル Can_Arduino_my.zip は こちらから ダウンロードできます

---------------------------------------------------------------------------------------------------------------
内容
(1) Prj0701AR
CANバスモニタ用Arduino版ノード。WIZ-C版から移植。Arduino+#258基板で作動。
(2) Prj0801AR
CANブリッジ用Arduino版ノード。WIZ-C版から移植。Arduino+#258基板で作動。
(3) Prj0901AR
複合アプリケーションのPC I/F用Arduino版ノード。WIZ-C版から移植。Arduino+#258基板で作動。
(4) Prj1001
第9章のCANバスに接続するArduinoノード(ノード#5)。サーボ、PWM制御用。Arduino+#258基板で作動。
----------------------------------------------------------------------------------------------------------------
この(1)を使いました。中にはP258_N1というフォルダがあり、この中にArduinoのソフトが収納されています。

この本でのCAN通信は125kのCAN通信を行っているサンプルなので、500kのJ1939には変更が必要です。
//--------------------------------------------------
  // CAN関係の初期化
  //CANInit(CAN_BRP_16MHz_125KBPS); // CLOCK 16MHz CAN 125bps
    CANInit(CAN_BRP_16MHz_500KBPS);    // CLOCK 16MHz CAN 500bps
//--------------------------------------------------
CAN通信だけは上記の変更だけで通信ができてしまいます。
(実は工作の間違いがあって四苦八苦したことは書きませんが・笑)

【シリアルモニタ】
Arduino IDE付属のシリアルモニタにCAN通信した結果が出るようにプログラミングされています。
Arduino_CAN_10.jpg

文字列の先頭4文字がCAN IDですが、4文字しかないということは標準IDです。
J1939は拡張IDを使っているので変更する必要があります。

・・・ここで色々調べました。
参考図書には拡張IDについて殆ど記述がありません。
しかし、巻末のMCP2515レジスタやバッファ仕様見ていると、拡張IDが受信できており、表示されていないだけではないかという推測に至りました。

IDに関するプログラム部分:オリジナル
//---------------------------------------
    // 標準IDの場合
    msg = ((word)MsgBuf[1] << 3);        // SIDH
    msg |= (MsgBuf[2] >> 5);                  // SIDL
//-----------------------------------------

参考にしたのはこちらです。seeedが提供しているCAN-Bus Sheild 用のサンプルソフトです。
中身を見ていくと ファイルの拡張子がcppなのでc++で書かれているようです。
//-------------------- 抜粋 ---------------
/*********************************************************************************************************
** Function name:           mcp2515_read_id
** Descriptions:            read can id
*********************************************************************************************************/
void MCP_CAN::mcp2515_read_id( const INT8U mcp_addr, INT8U* ext, INT32U* id )
{
    INT8U tbufdata[4];

    *ext = 0;
    *id = 0;

    mcp2515_readRegisterS( mcp_addr, tbufdata, 4 );

    *id = (tbufdata[MCP_SIDH]<<3) + (tbufdata[MCP_SIDL]>>5);

    if ( (tbufdata[MCP_SIDL] & MCP_TXB_EXIDE_M) ==  MCP_TXB_EXIDE_M )
    {
                                                                        /* extended id                  */
        *id = (*id<<2) + (tbufdata[MCP_SIDL] & 0x03);
        *id = (*id<<8) + tbufdata[MCP_EID8];
        *id = (*id<<8) + tbufdata[MCP_EID0];
        *ext = 1;
    }
}
//------------ ここまで -------------

MCP_SIDHとMCP_SIDL の処理までは標準IDの処理です。
次に続くのが拡張IDの処理の追加で、MCP_EID8,MCP_EID0が拡張IDが格納されている部分のようです。

私が使っているプログラムでは_EID8,_EID0に相当するデータは、標準ID格納バッファに続いていると推測し
//-------------------------------------
    if((MsgBuf[2] & 0x08) != 0x08) {         // 0x08というのは、MCP_TXB_EXIDE_M に当たります
     
      // 標準IDの場合
      msg = ((word)MsgBuf[1] << 3);        // SIDH
      msg |= (MsgBuf[2] >> 5);                   // SIDL
      sprintf(StrBuf, "T%04X", msg);         // メッセージ
      PUTSTR(StrBuf);
   
    }else if((MsgBuf[2] & 0x08) == 0x08) {
   
      //拡張IDの場合
      msg2 = ((long)MsgBuf[1] << 3);          // SIDH
      msg2 |= (MsgBuf[2] >> 5);                    // SIDL
      msg2 = (msg2 << 2) + (MsgBuf[2] & 0x03);
      msg2 = (msg2 << 8) +  MsgBuf[3];
      msg2 = (msg2 << 8) +  MsgBuf[4];

      msgL = (word)msg2;
      msgH = (word)(msg2 >> 16);
      sprintf(StrBuf, "T%04X", msgH);    // メッセージ
      PUTSTR(StrBuf);
      sprintf(StrBuf, "%04X-", msgL);    // メッセージ
      PUTSTR(StrBuf);
     
    }
//--------------------------------------
msg2という longの変数を設け、参考にしたソフトと同様の処理を、MsgBuf[3]、MsgBuf[4]に追加しました。
結果を表示するために、msgLとmsgHに分けました。

【プログラム修正後のシリアルモニタ】
Arduino_CAN_11.jpg

これで8文字の拡張IDが取得できました(CM用ツールで受信し確認してあります)

Tに続き8文字が拡張ID、D以降Sまでが経過時間、8がデータ数、続いてデータです。

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

1~3までは何とかなりそうだが、4.の後半は私のスキル上、とても厳しそうだ。


おっと、忘れていました。
139ドルもの大枚をはたいて購入したJ1939エミュレータなのですが、シリアルモニタをしながらCANデータを確認していて、何と! 6項目のボリューム調整ノブが付いているのに、変わるのはLCD上の数値だけです。CANデータが変わるのは3項目(RPM,VSS,ECT) しかありません。何という中華仕様・品質なのでしょうか。唖然となりました。

このことを記述しながら思いついたことがあります。
先に挙げたseeedのCAN_BUS_Sheildソフトですが、CANのSend / Write(送信)に関してもサンプルが充実しています。これをベースにしてJ1939エミュレータ・ボードを自作できそうな気もしてきました。
実機と同じデータ種別と間隔で変化できるエミュレータができると色々なチェック用途に使えそうです。




据置きヘッドホンアンプ用12VDC電源

これまで何台も据置きのバランス型ヘッドホンアンプ(こちら、http://higa284.blog20.fc2.com/blog-entry-275.html)を作ってきたが、これをラインアンプとして、これまたフル・バランス型パワーアンプ(こちら)で使って頂いている方がおります。

このヘッドホン(ライン)アンプで使っているのが12V電池電源(エネループ10本)なのですが、電池のメンテナンスが大変なので、ノイズの少ない電源がほしいとのこと。一時はパワーアンプ用電源と同構成の安定化電源を、との話も出たが、小出力のアンプにはもったいない。

そこで考えたのが、ぺるけさんヘッドホンアンプでも使っている低ノイズACアダプターにコモンモード・トランスを組み合わせる方法です。

【ACアダプタ】
PowerForLineAmp.jpg

秋月の12V出力ACアダプタを使っているが、入力が100-120V限定品のほうがノイズレベルが低いとあるので愛用してます。

【コモンモード・トランス】
PowerForLineAmp2.jpg

TDKのRSAL-2003AL という3Aタイプのものを使いました。出力部に50V3300uFのコンデンサ2個と、0.1uFセラミックコンデンサをパラっています。

【ノイズの比較】
PowerForLineAmp_10Khz.jpg

約100mAの出力になるように負荷抵抗を付け、ACレンジで計測した結果です。
横軸は0-10kHz、青がフィルター入力部で、赤が出力部です。

入力での最悪レベルが -75dB に対して、出力は -105dB 程度で、元々低レベルだったノイズですが、更に -30dB も改良されてます。(コモンモード・ノイズフィルターのカタログでは、この周波数帯では -25dBのようです)





金田式ベースのDCヘッドホンアンプ:定電流回路で音は変わる?

10月から検討していた、「金田式もどきベースの 低電圧 バランス型 DCヘッドホンアンプ」なのであるが、

1.いろいろとの試行錯誤の結果、やはり、基本回路部分の歪率がBADな低電圧はNOという結論に達しました。

2.バランス型にする必要性はあるのか?・・・・・ 今のアンバランス・パフォーマンスで十分です。

そんな訳で、「金田式もどきDCヘッドホンアンプ」の存続決定です。


こんなことを思いながら、試作したバランス型基板での音楽を聴いていたのですが・・・  
バランスではなく、アンバランス型にしてみたら、何か、ダイナミックさに欠ける・・・ 気がする・・・!
えーー!何で? だったら、結論2.がどこかに吹っ飛びます。


【金田式ベースの低電圧バランス型DC HPA 試作基板】

HPA_kane_DC_LCase_4.jpg

この基板、バランス型にした以外に、実は定電流回路を

・金田式:J-FET1個と抵抗によるもの。調整がピーキー   から
・本器 :J-FETと抵抗、及びカレントミラー回路による定電流回路。調整が穏やか  に変更してます。

まさか、このせいで音のダイナミックさが無くなってしまったのか?
まさか、まさかと思うのですが、心配になったら確認しないと黙っていられない性分です。

HPA_kane_DC_LCase_2.jpg

並んでいるのは、左から、試作のバランス/アンバランス切換型の金田式もどきDCヘッドホンアンプ
(カレントミラー定電流)

真ん中が、金田式もどきDCヘッドホンアンプ・オリジナル
右が、真ん中のものを、定電流をカレントミラーに換装したものです。

作るにはなかなか手間がかかりました。
Li-ion電池の保護や充電回路は付けてないのですが、夜分だけですが2日もかかってます。

でも、音質確認はあっというまでした。 

定電流回路をカレントミラーに変えてもダイナミックな音感は不変でした。
すなわち、しっかりとした基板で作った完成品では問題なくダイナミックな音が保証できます。


でも、何故試作モックアップ基板ではあんな大人しい音質になったのでしょうか。バランス型でばかり音を聴いていたので、最近まで判りませんでしたね。きっと、トランジスタ類を変換基板に載せているので、回路長が長くなったのが効いているのでしょうかね。謎です。


こんなに音質・音感に気を使っているには理由があります。
ダイナミックな音がする金田式もどきは非常に好評で、70台近い台数を作って頂きました。
非常に好評だったのですが、唯一の問題・弱点はアイドル電流調整がピーキーで安定性に欠けることです。これはこのタイプの定電流回路を使っている金田式アンプの弱点と思われます。

よって、作って頂いた皆さんには苦労をおかけしたと思います。
カレントミラータイプの定電流回路に変更することで、アイドル電流の調整が容易で且つ経時変化が少ないアンプになると期待しています。


この問題を払拭した上で、新たなヘッドホンアンプは、「アルミケースに収納する」ことです。(下図はタカチ MXAアルミケース)
HPA_kane_DC_LCase_6.jpg  HPA_kane_DC_LCase_5.jpg

このモバイルケースに入れれば、外部からの電気的ノイズの低減にもなります。デザイン性がGoodです。
これに、デザインした透明シールを貼れば、更にドレスアップができる筈です・

【これに収納するHPA基板図】
HPA_kane_DC_LCase_1.jpg

アルミケースが 82x94mm なので、基板も 72x88mm と大きくなり、Li-ion電池も基板上に置けます。

入力・出力・負帰還に使っているオーディオ抵抗の足曲げカーブも緩やかにできます。
電源コンデンサも2個から4個に増え、この図からは判りませんがコンデンサ長も11mmから14mmまでサイズアップが可能になります。

今、最も関心があることは、「アルミパネルのデザイン印刷」です。
ケースメーカーに依頼すればOKなのですが、それなりの高額加工費が発生します。
どうにか自分でできないものかと腐心してます。




バッテリードライブ式バランス型DCパワーアンプの製作

*** 2016年2月24日 このアンプ用にAC電源動作の安定化電源装置を製作した記事を追補しました ***

この5月と9月に、バッテリードライブ式バランス型DC_miniアンプを作ったが、これを2Wayの低音側に使った場合、音量が足らないという相談を受けた。低音専用となると3W出力では足りないということのようです。

パワーを上げるには電池電圧を上げるのが解決策になるのですが、電池の端子数が増えるのと、熱対策(ヒートシンク容量増)、そして万が一の故障に備えた保護回路が必須になります。

10月末に受けた依頼だったのですが、諸々時間がかかってやっとのことで音出しができました。

【バッテリー数を4個に増量し充電中】
AMP_kane_DC_BAL3_6.jpg

7.2V 7200mAh のリチウムイオン電池を4個使います。充電後は8.5V程度になるので、アンプへの供給電圧は±17V程度になります。

【充電器も4個用に新作】
AMP_kane_DC_BAL3_5.jpg

充電回路は12V1AのDCアダプタ出力に直列5Ω5W抵抗を入れてバッテリーに接続します。5Ω抵抗の両端電圧をLED点灯に使います。充電が完了すればバッテリ内部の保護回路でOFFされます。
4個充電になるので冷却ファンも2個使いました。

【新作のバランス型アンプ用の保護回路】
AMP_kane_DC_BAL3_7.jpg

この回路は、右側がオペアンプによるスピーカー出力DC検出回路、中央がバッテリーチェック回路、左側がDC検出によってC_MOS_FETによる電源スイッチをOFFさせる回路です。
オリジナルは金田式アンプの保護回路ですが、バランス型アンプに対応したオペアンプ回路の追加、オリジナルの NAND GATE、TC4011BPの耐圧が18V(絶対耐圧は20V)なのでトランジスタに変更してあります。

実を言うとこの回路がFixするまで少々時間がかかりました。LTSpiceで動作確認して決めました。

AMP_kane_DC_BAL3_8.jpg

手配線基板では面倒なのでエッチングで基板を作りました。太く大電流が流れる部分には銅のより線を貼り付けてあります。

【アンプ回路】
AMP_kane_DC_BAL3_0.jpg

miniアンプと基本的には同じ回路です。

AMP_kane_DC_BAL3_1.jpg

1%歪率での最大出力は約14Vp-pですので、推定出力は約12Wになります。

14Vp-pもの出力電圧になると、バランス型からアンバランスに変換する計測用アンプが対応できなくなります。
すなわち私のところでは歪率が計測できません。

【アンプ内部】
AMP_kane_DC_BAL3_4.jpg  

配線したばかりのアンプ内部です。

アンプケースにはトランジスタからの放熱に対応できるように、左右両側に放熱フィンが付いた、タカチ・HY型を用いました。
中央に保護回路、左右にアンプ基板、そこから放熱壁に付けたトランジスタに配線します。

------------------ AC電源駆動 安定化電圧電源装置の製作 --------------------

このハイパワーアンプは2Wayの低音域駆動用に使用しているとのこと。
リチウムイオン電池のライフが問題なのか判っていないのですが、AC電源駆動の電源装置のリクエストがあった。

【完成した電源内部】
DC_Power_1.jpg

タカチ・YM-350アルミケース(350x230x55mm)にぴったり収まってます。

左から、トロイダルトランス・AC100V,±15V,2A、両波整流シリコンダイオード、整流コンデンサ群 ±19,200μF、安定化電源回路 ±15.7V です。

やはりポイントになるのが安定化電源回路です。
【安定化電源回路図】 これは私が愛読しているメルマガの参考回路からいただきました。
DC_Power_8.jpg

例によって、LTSPiceを使って回路のチェックを行いました。素晴らしい安定度です。入力に±1V程度の変動を入れてみても、出力電圧はびくとも動きませんでした。

【安定化電源回路の外観】
DC_Power_4.jpg

LEDの発色がなんとも言えません。

DC_Power_5.jpg

安定化電源のキーとなるのが、プラス側マイナス側に設けた4個ずつのZenerダイオードです。±15Vが目標電圧なので、公称電圧5.1V品を3個づつ用意したのですが、規定電圧に届かず4個で15.7Vになりました。

【出力電圧のノイズ特性】
DC_Power_9.jpg

アンプが手元にないので無負荷状態でのノイズレベルです。平均で-100dBV程度、最悪-70dBV程度ですので十分でしょう。

本日お嫁に行きました。気立てが良いかどうかは後日の評価を待ちましょう。


金田式ベースの低電圧バランス型DCヘッドホンアンプ(検討)

役目を終えた 2Win差動式低電圧バランス型DC HPA テスト基板から、変換基板に貼り付けた Dual トランジスタ類が取り外されています。

HPA_kane_DC_BAL_1.jpg

取り外した Dual トランジスタ類は別のテスト基板に載せられています。
実は当初からこれも作りたかったので、取り外し可能にしておいたのです。

HPA_kane_DC_BAL_2.jpg

基板の左上側に黒いトランジスタが2個ずつ載っています。2SK170BL FET なんです。
多量に買い込んでおいた 2SK170BL を使おうという考えです。

駆動する電池は右側、単三電池2個の低電圧バージョンです。

はい、金田式もどき DC HPA回路をベースした、低電圧バージョン・バランス型ヘッドホンアンプです。

LTSpice回路図はこちらです。
HPA_kane_DC_BAL_5.jpg

この回路構成は一朝一夕に生まれたものではなく、実は2012年3月のHPA v4に端を発しています。
この記事を読まれれば判りますが、金田式の特徴である初段FET差動と終段同極性増幅回路を考えています。

低電圧なので初段FETは断念し、2段目から終段に繋がる部分を、「インバーテッド・ダーリントン接続見做し回路」を使っています。
よって上図の回路は、断念していた初段FET増幅回路を低電圧バージョンに持ち込んだものなのです。

昨今LTSPiceの結果を見て、やってみようという気になったのです。

LTSPiceの動作波形です。バランス出力ができてます。

HPA_kane_DC_BAL_6.jpg

ここでFETをよくご存じの方からは当然の疑問がでます。

「1.5V程度の電圧ではFETは正常動作しないでしょう」 とね。

そうなんです。実は私も最大の心配事がそれでした。

ここに示すのは2SK170のVDS (ドレイン-ソース間電圧)特性です。

HPA_kane_DC_BAL_7.jpg

3V近傍 (緑線) から直線領域になるので、それ以上の領域が作動推奨範囲です。
私が使おうとしている1.5V (実際には1.2V程度以下) は全くの曲線エリアです。これではリニアな増幅ができません。

FET差動ヘッドホンアンプで傑作を作られているぺるけさんのブログ記事でのロードラインでも

HPA_kane_DC_BAL_8.jpg
このように3V以上をお使いなので (青線)、私が考えている電池電圧でのロードライン(左下隅の赤線領域)は全くお話にならない領域になることは必定なのです。

しかし、何と、先のLTSPiceでの歪率計算結果は、0.077%程度 (入力0.5Vp-p、出力1.0Vp-p、75Ω負荷) です。
実際に作ってみた基板でも同様出力点で 0.08% 歪率でしたので結果を再現しています。

しかし、どうしてこんな結果オーライ特性が出るのでしょうか。LTSPiceの波形と出力でチェックしてみました。

【電池電圧 5.6V = FETロードライン 4V 近傍での特性】
ぺるけさんのFET差動増幅のロードライン近くになるように電池電圧を合わせてシミュレーションしてみます。

これはFETのVds電圧(緑、差動領域3.7-4.2V)と、バランス出力(赤)です。
HPA_kane_DC_BAL_9_40V.jpg

Vds間電圧特性はキレイなSin波です。

これはバランス出力の片側(out1)、片側(out2)出力です。
HPA_kane_DC_BAL_11_40V2.jpg

これもキレイなSin波です。

【電池電圧1.5V での特性】
同様にVds特性(緑)です。
HPA_kane_DC_BAL_10_15V.jpg

ロードラインは、上側1.3V-下側0.9V ですが、大分歪んでいます。でもバランス出力(赤)はかなりキレイなSin波です。

同様に、out1、out2出力とバランス出力です
HPA_kane_DC_BAL_12_15V2.jpg

out1,out2はSin波とはとても言えませんが、「位相が180度ずれた相似形」です。

バランス型の特性である相互出力が補間することで、結果的に歪率が良くなっている という解釈です。
即ち、バランス型なので成し得た結果と言えます。

こうなってくると、更にFETペア特性が重要になってきますね。VgsやIdssで合わせていますが、同一ロットも必須条件として考える必要があるでしょう。

【出音】

こんなイレギュラーなアンプですが、
最大の目的は、「金田式(とは既に言えないかな)系列の音」 がするのかという確認なのです。
・・・ほぼ一晩YouTubeでエージング後の再試聴です。電池電圧は1.4Vに下がってます。

金田式に近いメリハリのある出音です。パンチがありキラメキ感があります。
ゆったりとした朝には元気になる音、まったりとした夜には妖艶なキラキラ音 といった感じです。

長時間駆動(下記)できるので電池ライフを気にせず、自宅での音楽鑑賞にも十分使えるのではないでしょうか。

【回路図・片側】
HPA_kane_DC_BAL_3.jpg

定電流調整抵抗R107の82Ωは大きい方向に変わる可能性があります。

アイドル電流を半分5mA程度にしたバッテリー消費電流は20mA程度です。これならば電池ライフは100時間近くになります。
規定の10mA流しても電池ライフが50時間もありそうなので Good と思います。

【基板】
HPA_kane_DC_BAL_4.jpg

電源コンデンサ1つを左に配置し、バランスコネクタ位置を大きく開けました。

バランス型を一般の方はあまり作らないので小ロットで準備しようかと考えてます。

---------  2015/10/27 追記  ----------
基板を発注すべく準備をしていたが、スカスカ状態のバランスコネクタ取り付けエリアを見ていて思いつきました。

せっかくの金田式もどき構成のバランス型HPAなのであるのだから、単三乾電池の低電圧だけではなく Li-ion電池も搭載できればどうだろうか、という欲が出ました。

しかし充電回路やUSBコネクタはスペース的にとても無理なので、せめてLi-ion電池保護回路だけは付けてみました。
このHPAでは保護回路付き単三型Li-ion電池取り付けが厳しいので、最低保護回路が必要です。これが無いと過放電でLi-ion電池が壊れます。HPAで自己充電ができませんが、安価な充電器が売っていますので、それを使って頂こうと思います。

低電圧電池を使う場合は保護回路をジャンパーします。小型のスイッチを付ければ電池の交互使用も可能にできるのですが、スペース面やニーズ面を考えて止めました。

HPA_kane_DC_BAL_13.jpg

右下に青く見えているのが保護回路基板取り付け部で裏面に取り付けます。ジャンパーは表側です。
電池電圧が上がった場合でも、今の回路定数で動作するはずです。替えるのはLED用のチップ抵抗だけでOKのはずです。







プロフィール

haiga

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

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

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

この人とブロともになる

QRコード
QRコード