MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
MSP430F149 - CANBUSのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
MSP430F149 - CANBUS
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == <br><br> == システムの構成 == MSP430F149はマイクロコントローラとして全体を制御し、SPI通信を使用してMCP2515 CANコントローラーチップに命令を送信する。<br> MCP2515がCANプロトコルの処理を行い、その先にCANトランシーバ (TJA1050等) が物理層の信号変換を担当する。<br> <br> この3層構造により、MSP430F149のような汎用マイコンでもCAN通信 (CAN 2.0B通信) が可能になる。<br> <br> また、次のような拡張も可能である。<br> * フィルタとマスクを適切に設定することにより、特定のメッセージIDのみを受信する。 * 複数の送信バッファを使用して送信の優先度を制御する。 * エラーハンドリングを実装して、バスオフ状態からの回復を行う。 <br><br> == ハードウェア接続 == MSP430F149とMCP2515の接続は、MSP430F149のUSARTモジュールをSPIモードで使用する。 * MCP2515 VCC *: 3.3[V] または 5[V] (MCP2515の仕様に応じる) * MCP2515 GND *: GND * MCP2515 CS *: P3.0 (チップセレクト、任意のGPIOピン) * MCP2515 SO (MISO) *: P3.2 (SOMI0) * MCP2515 SI (MOSI) *: P3.1 (SIMO0) * MCP2515 SCK *: P3.3 (UCLK0) * MCP2515 INT *: P2.0 (割り込み用、任意のGPIOピン) * MCP2515 RESET *: P3.4 (リセット用、任意のGPIOピン) <br> <u>※注意</u><br> <u>MSP430F149は3.3[V]で駆動するが、MCP2515は5[V]駆動するモデルもある。</u><br> <u>そのため、電圧レベルが異なる場合はレベルシフタの使用を検討すること。</u><br> <br><br> == サンプルコード == ==== レジスタの定義 ==== MCP2515は多くのレジスタを持っており、それぞれが特定の機能を制御する。<br> <syntaxhighlight lang="c"> #include <msp430f149.h> #include <stdint.h> // MCP2515のレジスタアドレス定義 #define MCP_RXF0SIDH 0x00 #define MCP_RXF0SIDL 0x01 #define MCP_RXM0SIDH 0x20 #define MCP_RXM0SIDL 0x21 #define MCP_CNF3 0x28 #define MCP_CNF2 0x29 #define MCP_CNF1 0x2A #define MCP_CANINTE 0x2B #define MCP_CANINTF 0x2C #define MCP_EFLG 0x2D #define MCP_CANSTAT 0x0E #define MCP_CANCTRL 0x0F #define MCP_TXB0CTRL 0x30 #define MCP_TXB0SIDH 0x31 #define MCP_TXB0SIDL 0x32 #define MCP_TXB0DLC 0x35 #define MCP_TXB0DATA 0x36 #define MCP_RXB0CTRL 0x60 #define MCP_RXB0SIDH 0x61 #define MCP_RXB0SIDL 0x62 #define MCP_RXB0DLC 0x65 #define MCP_RXB0DATA 0x66 // MCP2515の命令セット // これらはSPI経由でMCP2515に送る命令コード #define MCP_WRITE 0x02 #define MCP_READ 0x03 #define MCP_BITMOD 0x05 #define MCP_LOAD_TX0 0x40 #define MCP_RTS_TX0 0x81 #define MCP_READ_RX0 0x90 #define MCP_READ_STATUS 0xA0 #define MCP_RESET 0xC0 // CANコントロールレジスタのモード設定 #define MODE_NORMAL 0x00 #define MODE_SLEEP 0x20 #define MODE_LOOPBACK 0x40 #define MODE_LISTENONLY 0x60 #define MODE_CONFIG 0x80 // ピン定義 #define CS_PIN BIT0 // P3.0 #define RESET_PIN BIT4 // P3.4 #define INT_PIN BIT0 // P2.0 // チップセレクトのマクロ #define CS_LOW() (P3OUT &= ~CS_PIN) #define CS_HIGH() (P3OUT |= CS_PIN) #define RESET_LOW() (P3OUT &= ~RESET_PIN) #define RESET_HIGH() (P3OUT |= RESET_PIN) </syntaxhighlight> <br> ==== SPI通信 ==== SPI通信は、MSP430F149とMCP2515の間のデータ転送手段である。<br> MSP430F149のUSART0モジュールをSPIモードで設定する必要がある。<br> <br> SPIは同期式のシリアル通信であり、クロック信号に同期してデータを送受信する。<br> マスター (MSP430F149) がクロックを生成して、スレーブ (MCP2515) がそれに従う。<br> 全2重通信のため、1バイト送信すると同時に1バイト受信することになる。(SPI_Transfer関数で送受信を同時に行っている理由)<br> <br> <syntaxhighlight lang="c"> // SPI通信の初期化関数 // MSP430F149のUSART0をSPIマスターモードで設定する void SPI_Init(void) { // USART0をSPIモードで初期化 // まず、USARTを無効化してから設定を行う U0CTL = SWRST; // USARTをリセット状態に // SPI設定:マスターモード、8ビットデータ、MSBファースト // 3ピンモード、同期モード (SPI) U0CTL |= CHAR + SYNC + MM; // クロック極性とフェーズの設定 // CKPL=0, CKPH=1はMCP2515の要求に合わせた設定である U0TCTL = CKPL + SSEL1 + SSEL0 + STC; // SMCLK使用、3ピンモード // ボーレート設定 // SMCLK = 8[MHz] の場合、分周比を2に設定して4[MHz]動作 // MCP2515は最大10[MHz]まで対応しているが、安全マージンを取る U0BR0 = 0x02; U0BR1 = 0x00; U0MCTL = 0x00; // モジュレーション無効 // ピンの機能設定 // P3.1, P3.2, P3.3をSPI機能として使用 P3SEL |= BIT1 + BIT2 + BIT3; // USARTを有効化 ME1 |= USPIE0; U0CTL &= ~SWRST; // リセット解除 } // SPI経由で1バイト送受信する関数 uint8_t SPI_Transfer(uint8_t data) { // 送信バッファが空になるまで待機 // これにより前回の送信が完了していることを確認 while (!(IFG1 & UTXIFG0)); // 1バイトのデータを送信 U0TXBUF = data; // 受信完了まで待機 // SPIは全2重通信のため、1バイト送信すると同時に1バイト受信する while (!(IFG1 & URXIFG0)); // 受信したデータを返す return U0RXBUF; } </syntaxhighlight> <br> ==== MCP2515の初期化とCAN設定 ==== CAN通信の核心部分である。<br> MCP2515を初期化して、CANバスの通信速度やフィルタ等を設定する。<br> <br> ※ビットレート設定について<br> CANバスの通信速度は重要であり、ネットワーク上の全てのノードが同じ速度に設定されている必要がある。<br> 以下の例では、500[kbps]に設定しているが、これは産業用途で用いられる一般的な速度となる。<br> また、125[kbps]、250[kbps]、1[Mbps]等も広く使用されている。<br> <br> <syntaxhighlight lang="c"> // MCP2515を初期化してCAN通信を準備する // この関数はプログラム起動時に1回だけ呼び出す uint8_t MCP2515_Init(void) { // ハードウェアリセット (オプション) RESET_LOW(); __delay_cycles(10000); RESET_HIGH(); __delay_cycles(10000); // ソフトウェアリセット MCP2515_Reset(); // コンフィグレーションモードに入る // このモードでのみ、ビットレートなどの重要な設定が変更できる MCP2515_SetMode(MODE_CONFIG); // CAN通信速度の設定 (500[kbps] @ 8[MHz]水晶振動子) // この設定は使用する水晶振動子の周波数に依存する // ビットタイミングの計算は複雑であるが、以下のものは実績のある設定値である // CNF1: BRP=0 (分周比1)、SJW=00 (1TQ) MCP2515_Write(MCP_CNF1, 0x00); // CNF2: BTLMODE=1、サンプルポイント設定 // PHSEG1=110 (7TQ)、PRSEG=001 (2TQ) MCP2515_Write(MCP_CNF2, 0xD1); // CNF3: PHSEG2=010 (3TQ)、ウェイクアップフィルタ無効 MCP2515_Write(MCP_CNF3, 0x02); // これらの設定により、次のビットタイミングが実現される: // 1ビット = 1TQ + PRSEG + PHSEG1 + PHSEG2 = 1 + 2 + 7 + 3 = 16TQ // ビットレート = 8MHz / (2 * (BRP+1) * 16) = 500[kbps] // 受信フィルタとマスクの設定 // 全てのメッセージを受信するように設定 (フィルタ無効) MCP2515_Write(MCP_RXB0CTRL, 0x60); // 受信バッファ0 : 全て受信、ロールオーバー有効 // マスクを0に設定すると、全てのIDを受け入れる MCP2515_Write(MCP_RXM0SIDH, 0x00); MCP2515_Write(MCP_RXM0SIDL, 0x00); // 割り込み設定 // 受信割り込みを有効化 MCP2515_Write(MCP_CANINTE, 0x01); // RX0IE: 受信バッファ0割り込み有効 // ノーマルモードに移行してCAN通信を開始 MCP2515_SetMode(MODE_NORMAL); // 初期化が成功したか確認 if ((MCP2515_Read(MCP_CANSTAT) & 0xE0) == MODE_NORMAL) { return 1; // 成功 } return 0; // 失敗 } </syntaxhighlight> <br> ==== MCP2515との通信 ==== MCP2515の特定のレジスタを読み書きする。<br> <syntaxhighlight lang="c"> // MCP2515のレジスタから1バイト読み取る uint8_t MCP2515_Read(uint8_t address) { uint8_t data; CS_LOW(); // チップセレクトをアクティブに SPI_Transfer(MCP_READ); // 読み取り命令を送信 SPI_Transfer(address); // レジスタアドレスを送信 data = SPI_Transfer(0x00); // ダミーデータを送信してレジスタ値を受信 CS_HIGH(); // チップセレクトを非アクティブに return data; } // MCP2515のレジスタに1バイト書き込む void MCP2515_Write(uint8_t address, uint8_t data) { CS_LOW(); // チップセレクトをアクティブに SPI_Transfer(MCP_WRITE); // 書き込み命令を送信 SPI_Transfer(address); // レジスタアドレスを送信 SPI_Transfer(data); // データを送信 CS_HIGH(); // チップセレクトを非アクティブに } // MCP2515のレジスタの特定ビットを変更する関数 // マスクで指定したビットのみを変更できるため、他のビットに影響を与えずに設定を変更できる void MCP2515_BitModify(uint8_t address, uint8_t mask, uint8_t data) { CS_LOW(); SPI_Transfer(MCP_BITMOD); // ビット変更命令 SPI_Transfer(address); // レジスタアドレス SPI_Transfer(mask); // 変更するビットのマスク SPI_Transfer(data); // 新しい値 CS_HIGH(); } // MCP2515のモード設定 // コンフィグモード、ノーマルモードなどを切り替える void MCP2515_SetMode(uint8_t mode) { // CANCTRLレジスタの上位3ビットでモードを設定 MCP2515_BitModify(MCP_CANCTRL, 0xE0, mode); // モード変更が完了するまで待機 // CANSTATレジスタを読み取って確認します uint8_t timeout = 255; while ((MCP2515_Read(MCP_CANSTAT) & 0xE0) != mode && timeout > 0) { __delay_cycles(1000); // 短い遅延 timeout--; } } // MCP2515をリセットする void MCP2515_Reset(void) { CS_LOW(); SPI_Transfer(MCP_RESET); // リセット命令を送信 CS_HIGH(); __delay_cycles(10000); // リセット後の安定待ち(約1ms) } </syntaxhighlight> <br> ==== CANメッセージの送信 ==== CANメッセージを送信する。<br> <br> 送信プロセスを以下に示す。<br> # まず、メッセージIDを設定する。 # 次に、データ長を指定する。 # 実際のデータを書き込む。 # 最後に、送信要求を出す。 <br> MCP2515は送信バッファを持っているため、複数のメッセージを効率的に処理することができる。<br> <br> <syntaxhighlight lang="c"> // CANメッセージ送信関数 // id : CANメッセージID (標準フォーマット11ビット) // dlc : データ長(0~8バイト) // data : 送信するデータの配列 uint8_t CAN_SendMessage(uint16_t id, uint8_t dlc, uint8_t *data) { // 送信バッファが空いているか確認 // TXB0CTRLレジスタのTXREQビットが0なら送信可能 if (MCP2515_Read(MCP_TXB0CTRL) & 0x08) { return 0; // 送信バッファが使用中、送信失敗 } // メッセージIDの設定 // 11ビットの標準IDを2つのレジスタに分割して格納 // 上位8ビット (ID10-ID3) をSIDHレジスタに MCP2515_Write(MCP_TXB0SIDH, (uint8_t)(id >> 3)); // 下位3ビット (ID2-ID0) をSIDLレジスタの上位3ビットに // ビット4はEXIDE (拡張ID有効ビット) で、標準フォーマットなので0 MCP2515_Write(MCP_TXB0SIDL, (uint8_t)(id << 5)); // データ長コード (DLC) の設定 // 下位4ビットがデータ長を表します(0~8) if (dlc > 8) dlc = 8; // 最大8バイトに制限 MCP2515_Write(MCP_TXB0DLC, dlc); // データの書き込み // 送信するデータをデータレジスタに順次書き込む for (uint8_t i = 0; i < dlc; i++) { MCP2515_Write(MCP_TXB0DATA + i, data[i]); } // 送信要求 // TXB0CTRLレジスタのTXREQビットを1にセットして送信開始 MCP2515_BitModify(MCP_TXB0CTRL, 0x08, 0x08); // 送信完了待ち (オプション) // 実際のアプリケーションでは割り込みを使用することが多い uint16_t timeout = 1000; while ((MCP2515_Read(MCP_TXB0CTRL) & 0x08) && timeout > 0) { __delay_cycles(100); timeout--; } if (timeout == 0) { return 0; // タイムアウト、送信失敗 } return 1; // 送信成功 } </syntaxhighlight> <br> ==== CANメッセージの受信 ==== MCP2515は受信したメッセージを内部バッファに格納するため、それを読み出す必要がある。<br> <br> 受信では、まず受信バッファにメッセージが存在するかを確認して、存在する場合はメッセージID、データ長、データ本体を順次読み出す。<br> 最後に受信フラグをクリアすることにより、次のメッセージの受信に備える。<br> <br> <syntaxhighlight lang="c"> // CANメッセージ受信 // 受信バッファにメッセージがある場合、それを読み出す // id : 受信したメッセージIDを格納するポインタ // dlc : 受信したデータ長を格納するポインタ // data : 受信データを格納する配列 uint8_t CAN_ReceiveMessage(uint16_t *id, uint8_t *dlc, uint8_t *data) { // 受信バッファにメッセージがあるか確認 // CANINTFレジスタのRX0IFビットが1なら受信あり if (!(MCP2515_Read(MCP_CANINTF) & 0x01)) { return 0; // 受信メッセージなし } // メッセージIDの読み取り // SIDHレジスタから上位8ビットを取得 uint8_t sidh = MCP2515_Read(MCP_RXB0SIDH); // SIDLレジスタから下位3ビットを取得 uint8_t sidl = MCP2515_Read(MCP_RXB0SIDL); // 11ビットの標準IDを復元 // SIDH (8ビット) を左に3ビットシフトし、SIDLの上位3ビットを加算 *id = (uint16_t)(sidh << 3) | (uint16_t)(sidl >> 5); // データ長の読み取り *dlc = MCP2515_Read(MCP_RXB0DLC) & 0x0F; // 下位4ビットがDLC // データの読み取り // 受信したデータをデータレジスタから順次読み出す for (uint8_t i = 0; i < *dlc; i++) { data[i] = MCP2515_Read(MCP_RXB0DATA + i); } // 受信割り込みフラグをクリア // 次のメッセージを受信できるようにする MCP2515_BitModify(MCP_CANINTF, 0x01, 0x00); return 1; // 受信成功 } </syntaxhighlight> <br> ==== メイン処理 ==== まず、全てのハードウェアを初期化して、その後無限ループの中で定期的にメッセージを送信し、受信を確認する。<br> <br> 実務では、送信タイミングや受信メッセージの処理内容は用途に応じて変更する必要がある。<br> <br> <syntaxhighlight lang="c"> volatile uint8_t message_received = 0; // 受信フラグ int main(void) { // ウォッチドッグタイマの停止 WDTCTL = WDTPW + WDTHOLD; // クロック設定 (DCO = 8MHz) DCOCTL = CALDCO_8MHZ; BCSCTL1 = CALBC1_8MHZ; // GPIO初期化 P3DIR |= CS_PIN + RESET_PIN; // CS、RESETピンを出力に設定 P3OUT |= CS_PIN; // CS初期状態はHIGH(非選択) P3OUT |= RESET_PIN; // RESET初期状態はHIGH (非リセット) P2DIR &= ~INT_PIN; // INTピンを入力に設定 P2IE |= INT_PIN; // INTピンの割り込みを有効化 P2IES |= INT_PIN; // 立ち下がりエッジで割り込み P2IFG &= ~INT_PIN; // 割り込みフラグをクリア // SPI初期化 SPI_Init(); // MCP2515初期化 if (!MCP2515_Init()) { // 初期化失敗時の処理 // 例 : LEDを点滅させる等 while(1) { __delay_cycles(8000000); // 無限ループ } } // グローバル割り込み有効化 __enable_interrupt(); // メッセージ送信の例 uint16_t tx_id = 0x123; // 送信メッセージID uint8_t tx_dlc = 8; // データ長8バイト uint8_t tx_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; // 受信用変数 uint16_t rx_id; uint8_t rx_dlc; uint8_t rx_data[8]; // メインループ while(1) { // 定期的にメッセージを送信(例:1秒ごと) if (CAN_SendMessage(tx_id, tx_dlc, tx_data)) { // 送信成功時の処理 // ...略 } // メッセージ受信のチェック if (message_received) { message_received = 0; // フラグをクリア // メッセージを読み出し if (CAN_ReceiveMessage(&rx_id, &rx_dlc, rx_data)) { // 受信したメッセージの処理 // 例 : 受信IDに応じて異なる処理を行う switch(rx_id) { case 0x100: // ID 0x100のメッセージを処理 break; case 0x200: // ID 0x200のメッセージを処理 break; default: // その他のメッセージの処理 break; } } } // 次の送信まで待機 (約1秒) __delay_cycles(8000000); } return 0; } // MCP2515の割り込みハンドラ // INTピンが立ち下がるとこの関数が呼ばれる #pragma vector=PORT2_VECTOR __interrupt void Port2_ISR(void) { if (P2IFG & INT_PIN) { // 受信フラグをセット message_received = 1; // 割り込みフラグをクリア P2IFG &= ~INT_PIN; } } </syntaxhighlight> <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:MSP430]]
MSP430F149 - CANBUS
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse