OBDメーターの自作がうまく行ったので、作り方の紹介です。
使用したマイコンは AVR の ATMEGA です。ATMEGAにはCANが搭載されていないのでMicrochipのMCP2515を使ってCAN通信します。
他にCAN搭載のPICマイコンが有りますが、AVR にはBASICでプログラムが書ける BASCOM-AVRというコンパイラが有るので、今回はAVR と BASCOMの組み合わせで作成しました。
部品
マイコン → ATMEGA 250円
http://akizukidenshi.com/catalog/g/gI-03142/
CANコントローラー → MCP2515 190円
http://btos.jp/index.php?main_page=product_info&products_id=31
CANトランシーバー → MCP2551 100円
http://btos.jp/index.php?main_page=product_info&products_id=38&zenid=481dbdda98af925014d5308e1216cab4
LCD → お好みで
http://btos.jp/index.php?main_page=product_info&products_id=54
クリスタル → 20Mhz
コンデンサ → 少々
抵抗 → 少々
ダイオード → LCDのコントラスト用 → 1個
写真の状態でOBDⅡデータを取得して表示しています。
CAN通信は500kbpsと高速でデリケートそうですが、実際はブレットボードに組んだだけで車両と通信出来てしまいます。
マイコンとCANコントローラー間のSPI通信も10Mhz(2クロック)で動かしていますが、大丈夫でした。(20Mhz駆動のマイコンが10Mhzの通信を処理出来るってすごいですね。)
ハードウェアのハードルは結構(かなり)低いです。
回路はこんな感じになります。
マイコンはクリスタルで20Mhzで駆動して、クロックOUTを有効にするためにヒューズビットの書き換えが必要です。
Fuse Low Byte B7
Fuse High Byte DF
Extended Fuse Byte F9 今この設定で作動していますが、内容を精査していないのでもう少し良いセッティングもあるかもしれません。取り敢えず動きます。
CANコントローラーは、RESET端子を電源電圧で吊っておかないと動かないので注意が必要です。
CANトランシーバーのRS端子は、信号のHi Lo の変化のスロープが変化するそうです。
MCP2515の INT RX0BF RX1BF TXxRTS の端子は繋げなくても大丈夫ですが、RX0BFだけ接続した方がプログラムが書き易くなるので接続しました。
プログラムは、
’マイコンの宣言をして
$regfile = "m328pdef.dat"
$crystal = 20000000
’LCDを初期化して
Config Lcdpin = Pin , Rs = Portc.0 , E = Portc.1 , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5
Config Lcd = 16 * 2
Initlcd
’SPIも初期化
Config Spi = Hard , Interrupt = Off , Data Order = Msb , Master = Yes , Polarity = Low , Phase = 0 , Clockrate = 4 , Noss = 1
Reset Spcr.spr1 : Reset Spcr.spr0 : Set Spsr.spi2x
Spiinit
’SSピンの設定をして
Ss_mcp2515 Alias Portb.2
Config Ss_mcp2515 = Output
Set Ss_mcp2515
’RX0BFピンを設定。
Rx0bf Alias Pind.6
Config Rx0bf = Input
Set Rx0bf
これで準備が出来ました。
で、まずは、MCP2515をリセットしましょう。
リセットはSPIで 1byte のコマンドが有るので 1byte送信すればOKです。
Byte_data_spi(1) = &HC0 ← コマンド代入
Reset Ss_mcp2515 ← SSピン有効
Spiout Byte_data_spi(1) , 1 ← データ送信
Set Ss_mcp2515 ← SSピン無効
Waitms 100
続いて、MCP2515のCANの通信設定やマスク・フィルターの設定です。
書き込みコマンドは00000010なので、1byte目に00000010を入れて
2byte目はアドレスを入れます。 3byte目からは各レジスタに入れたいデータを入れておき、
Byte_data_spi(1) = &B00000010 'command Write
Byte_data_spi(2) = &B00000000 'address
Byte_data_spi(3) = &HFD 'RXF0 SID H
Byte_data_spi(4) = &H00 'RXF0 SID L
Byte_data_spi(5) = &H00 'RXF0 EID 8
Byte_data_spi(6) = &H00 'RXF0 EID 0
Byte_data_spi(7) = &H00 'RXF1 SID H
Byte_data_spi(8) = &H00 'RXF1 SID L
Byte_data_spi(9) = &H00 'RXF1 EID 8
Byte_data_spi(10) = &H00 'RXF1 EID 0
Byte_data_spi(11) = &H00 'RXF2 SID H
Byte_data_spi(12) = &H00 'RXF2 SID L
Byte_data_spi(13) = &H00 'RXF2 EID 8
Byte_data_spi(14) = &H00 'RXF2 EID 0
Byte_data_spi(15) = &B00001111 'BFP CTRL
Byte_data_spi(16) = &B00000000 'TXRTS CTRL
スレーブセレクトピンを下げて
Reset Ss_mcp2515
SPI データ送出実行。
Spiout Byte_data_spi(1) , 16 ←Spiout Byte_data_spi(1) ~(16) までの16byte送られます
終わったらSSピンを上げて送信完了です。
Set Ss_mcp2515
続けて アドレス00010000の設定
Byte_data_spi(2) = &B00010000 'address
Byte_data_spi(3) = &H00 'RXF3 SID H
Byte_data_spi(4) = &H00 'RXF3 SID L
Byte_data_spi(5) = &H00 'RXF3 EID 8
Byte_data_spi(6) = &H00 'RXF3 EID 0
Byte_data_spi(7) = &H00 'RXF4 SID H
Byte_data_spi(8) = &H00 'RXB4 SID L
Byte_data_spi(9) = &H00 'RXF4 EID 8
Byte_data_spi(10) = &H00 'RXF4 EID 0
Byte_data_spi(11) = &H00 'RXF5 SID H
Byte_data_spi(12) = &H00 'RXB5 SID L
Byte_data_spi(13) = &H00 'RXF5 EID 8
Byte_data_spi(14) = &H00 'RXF5 EID 0
’SPI送信
Reset Ss_mcp2515
Spiout Byte_data_spi(1) , 14
Set Ss_mcp2515
以下 レジスタの設定が続きます。
Byte_data_spi(2) = &B00100000 'address
Byte_data_spi(3) = &HFF 'RXM0SID H
Byte_data_spi(4) = &HE0 'RXM0SID L
Byte_data_spi(5) = &H00 'RXM0EID 8
Byte_data_spi(6) = &H00 'RXM0EID 0
Byte_data_spi(7) = &HFF 'RXM1SID H
Byte_data_spi(8) = &HE0 'RXM1SID L
Byte_data_spi(9) = &H00 'RXM1EID 8
Byte_data_spi(10) = &H00 'RXM1EID 0
Byte_data_spi(11) = &B00000011 'CNF3 SEG2=4 ⬅︎この辺が通信速度の設定
Byte_data_spi(12) = &B10110111 'CNF2 SEG1=7 PRESEG=8
Byte_data_spi(13) = &B10000000 'CNF1 SJW=3 BRP=100nS
Byte_data_spi(14) = &B00000000 'Canint E ⬅︎ エラー時割り込み無し
Byte_data_spi(15) = &B00000000 'Canint F
Byte_data_spi(16) = &B00000000 'EFLG
Reset Ss_mcp2515
Spiout Byte_data_spi(1) , 16
Set Ss_mcp2515
Byte_data_spi(2) = &B01100000 'Address
Byte_data_spi(3) = &B00100000
Reset Ss_mcp2515
Spiout Byte_data_spi(1) , 3
Set Ss_mcp2515
Byte_data_spi(2) = &B01110000
Byte_data_spi(3) = &B00100000
Reset Ss_mcp2515
Spiout Byte_data_spi(1) , 3
Set Ss_mcp2515
ここまでで設定完了です。
最後にCANCTRレジスタのREQOPに000を書き込むと CANが開通します。
Byte_data_spi(2) = &B00001111 'CANCTRL
Byte_data_spi(3) = &B00000000
Reset Ss_mcp2515
Spiout Byte_data_spi(1) , 3
Set Ss_mcp2515
CANが開通したので OBDⅡのデータを要求してみます。
要求のデータの送信手順は送信バッファにデータを書き込んでから、送信要求を出すの2つの手順で送信します。
送信バッファへの書き込みはコマンドが用意されていて、TXB0への書き込みはは01000000なので 1byte目に01000000を入れて
2byte目はOBDⅡのCAN_IDを入れますが、レジスタが1bit シフトしていますので、気を付けてください。7E0は FCに成ります。
⑶⑷⑸byte目は29bit IDの時に使用します。今回は11bitのIDを使用するので0で良いです。
⑹byte目はDLC、OBDⅡでは常に8byteを送信するので8で固定です。
⑺ここからOBDⅡの書式に成ります。データを要求するにはサービスNo.とPIDの2byteを送信するので 2
⑻サービスNo.の 1
⑼PID 水温は 5 です。
送信バッファの書き込み
Byte_data_spi(1) = &B01000000 'COMMAND TXB0SIDH
Byte_data_spi(2) = &HFC '7E0
Byte_data_spi(6) = &H08 'DLC
Byte_data_spi(7) = &H02 'OBD 送信バイト数
Byte_data_spi(8) = &H01 'Service_1
Byte_data_spi(9) = &H05 '水温
Reset Ss_mcp2515
Spiout Byte_data_spi(1) , 14
Set Ss_mcp2515
TXB0送信バッファの送信要求コマンドはB10000001なので
Byte_data_spi(1) = &B10000001
Reset Ss_mcp2515
Spiout Byte_data_spi(1) , 1
Set Ss_mcp2515
これで送信が実行されます。
OBD2の書式でデータ要求したので、今度は受信します。
受信バッファーRXB0はCAN_ID 7E8が入ってくるように設定してあります。RXB0にデータが入るとMCP2515のBX0BFピンが下がる(そう設定してある)ので、マイコンでポーリングして受信を認識します。
受信バッファ読み込みのコマンドは10010000 なので、コマンド送信後にSPIIN命令で13byte読み込むと受信バッファのデータが読み出せます。
If Rx0bf = 0 Then
Byte_data_spi(1) = &B10010000
Reset Ss_mcp2515
Spiout Byte_data_spi(1) , 1 ←受信バッファ読み込みコマンド 送信
Spiin Byte_data_spi(1) , 13 ←(1)~(13) に受信バッファのデータ読み込み
Set Ss_mcp2515
少し省略しちゃいますが、水温のデータは Byte_data_spi(9)に入っているので、
Byte_data_spi(9) = Byte_data_spi(9) - 40 ←物理値変換の式
Str_temp = Str( Byte_data_spi(9) ) ←文字列変換
Lcd Format(str_temp , " 0" ) ←LCDに右寄せで表示
こんな感じで OBD2のデータが取れます。
OBDⅡのサービス と PID は
wikipediaに詳しく乗っていましたので、他のデータもPIDの番号を変えればデータが取得出来ます。
モニターの他に、サービスの番号を変えれば自己診断の確認やRESETも出来ます。
〜追記〜
BASOMなので、データの配列を準備しておけばSPI(IN・OUT)関数で複数バイトをいっぺんに送受信出来てしまいます。 C言語だとマクロとか関数を作成する事になるのかな?