CANインターフェースを備えたデバイスを開発する場合、ネットワーク上のメッセージを追跡するための便利なツールが必要です。 RS232 / 485には、多くの低予算USBアダプターとさまざまなソフトウェアがありますが、CANには安価なターンキーソリューションが見つかりませんでした。
同時に、運転者のウェブサイトには、車のCANバスに接続するための自家製のデバイスがありました。 完成したプロジェクトの1つは、Atmega + SJA1000に実装されたUSB <> CANバスインターフェイス(CAN Hacker)と、STM32F105に実装されたSTM32-CAN-Busadapterプロジェクトでした。 彼らはCAN Hackerプログラムで動作します。これは仕事に十分便利なようです。 USBを介したコマンドプロトコルの簡単な分析により、これらのデバイスはCOMポートで表され、さらに通信がASCII文字からコマンドを送信する形式で行われることが示されました。
ビン内でSTM32VLDiscoveryボードが見つかり、これがテストの対象になりました。 その上で「STM32-CAN-Busadapter」を繰り返します。
最初のステップは、STM32VLDiscoveryにインストールされているSTM32F100マイクロコントローラーを交換することです。 実際、F1シリーズのCANとUSBの同時操作は、STM32F105 / 107マイクロコントローラーでのみ可能です。 STMには、さまざまなシリーズのマイクロコントローラーに対してピン間互換性がありますが、同じパッケージで作られているのは良いことです。
地元の店で購入しました:
1. STM32F105RBT6 297こする。
2. PCA82C250T 115こする。
3. TJA1055T 138こする。
4. PBS-40、2個。 114こする
「2.54ホール」ブレッドボードは、ずっと前から翼で待っていました。
速くしようとする
STM32VLDiscoveryからほこりを消去し、Demoプロジェクトをロードして、まだ機能することを確認します。 コントローラーを再はんだ付けし、同じプロジェクトをダウンロードして移植が成功したことを確認します。
STM32-CAN-Busadapterプロジェクトサイトから、バイナリファームウェアファイルをダウンロード(登録が必要)し 、「STM32 ST-LINKユーティリティ」の助けを借りて、それをコントローラーに縫い付けます。
簡略図は次のようになります。 より詳細な情報は、STM32-CAN-BusadapterプロジェクトのWebサイトで入手できます。
スキームに従ってUSB D +、D-、Vbusをはんだ付けします。 ジャンパー/スイッチをPA2に追加します。作成者の名前は「ブートローダー」です。
電源を入れても....、 何も機能せず、デバイスはUSBを介してまったく検出されません。 任意の位置で「ブートローダー」。
USB接続を判別するには、1.5 Kの抵抗を介してD +ラインを5 Vまでプルアップする必要があることを思い出してください。 その後、デバイスはvid / pid 0000で「不明なデバイス」として識別され始めます。
その後、何が起こっているのかを理解しようとするのに数時間かかり、USB接続をテストするためのテストファームウェアを作成することにしました。
USBをチェックするためのテストファームウェアを書いています
テストファームウェアを作成するには、STM32CubeMXを使用します。これにより、テストファームウェアを迅速に構築できます。 STとディストリビューターによると、CubeMXの使用は「ファッショナブルでスタイリッシュ、若者」ですが、いつかこのCubに対処する必要があります。
STMicroelectronicのWebサイトから、STM32CubeMXをダウンロードします。 バージョンは定期的に更新され、v4.7.0があります。
インストールされたキューブで、「ヘルプ」->「新規ライブラリのインストール」に進み、「ファミリSTM32F1のファームウェアパッケージ」をインストールします(V1.0.0があります)。
[ヘルプ]-> [アップデーター設定]で、[リポジトリフォルダー]-さまざまなデバッグボードのソースコードの例を含む「ファームウェアパッケージ」がダウンロードされた場所を確認できます。
Cubeで新しいプロジェクトを作成する
MCU-STM32F105RBTx。
「Configuration」->「Peripheals」->「RCC」で、外部水晶振動子からクロックを選択し、HSEを「Crystal / Ceramic Resonator」に設定します。
[構成]-> [ペリフェラル]-> [USB_OTG_FS]で[Device_Only]モードを選択し、[Activate_VBUS]チェックボックスを設定して、USB接続がいつ行われるかを自動的に決定します。 その後、USBで動作するようにレッグPA9、PA11、PA12を自動的に割り当てます。
[構成]-> [ミドルウェア]-> [USB_DEVICE]-> [FS IPのクラス]で、[通信デバイスクラス(仮想ポートCom)]を選択します。
次に、「クロック構成」タブで、マイクロコントローラーのクロックシステムを構成します。 SystemClock_Configプロシージャを調べることで、サンプルの既製のPLLおよびプリスケーラ係数を確認できます。 この「画像」を取得する必要があります。
これで、コンパイル用のプロジェクトを生成できます。
第一世代の前に、プロジェクトの名前、プロジェクトの場所、およびプロジェクトが形成されるIDEの入力を求められます。 使い慣れたものとしてKeil 4を選びました。 オプションは、Keil5、Keil4、IAR、TrueStudio、SW4STM32です。 生成後、「プロジェクトを開く」をクリックすると、開発環境が開きます。 何も変更せずに、コンパイルしてロードします。
そして... それは動作します。 デバイスが決定され、ドライバーがSTサイトで見つかりました。 「デバイスマネージャー」に「STMicroelectronics Virtual COM Port(COM4)」と表示されます。
次に、ハードウェアが機能する理由を理解するのに時間がかかりましたが、他の誰のファームウェアも理解しませんでした。 その結果、バイナリファイルの外観が異なることがわかりました。
プログラムの最初に割り込みベクトルがあり、ファームウェアに同様のものがあり、ダウンロードしたファームウェアでは、データがアドレスジャンプコマンドにまったく似ていないことを覚えています。
さらに、Googleはファームウェアの最初の4バイトがスタックアドレスであり、次の4バイトが最初のプログラムコマンドのアドレスであることを提案しました。
著者STM32-CAN-Busadapterに書き込みました。 ファームウェアが「壊れている」、機能しない、最初のバイトが本来のバイトと同じではない、と説明しました。 アンドレアスは私に答えた。 ファームウェアは機能しているが、独自のブートローダーが必要だと書きました。 .hexファイル「個人専用バージョン」がレターに添付されました。
わかりました、仕組みを見て
CANトランシーバーのマイクロ回路を完成させ、まさにそのような「美しさ」を手に入れました。 D + USBラインへのプルアップ抵抗は削除できます。これはマイクロコントローラー内にあります。
点滅、CAN Hackerの起動、学習。 ここで、CANネットワークに接続する必要があります。 CANメッセージを発行するSTM32F107コントローラーを備えたopenmcuボードがありました。 CAN Hackerプログラムで遊んだ後、モニターモードとトレーサーモードがあることに気付きました。メッセージはテーブルまたはリストに表示されます。
これは私のものではなく、短いビデオです。
これで、アダプター用の独自のファームウェアを作成することができます。 さらに、ファームウェアのブランクはすでにあります。
ファームウェアを書いています...
IDEでCubeによって生成されたプロジェクトを開き、不足しているコードを追加します。
基本的なルールは、
/ *ユーザーコードの開始... * /
そして
/ *ユーザーコード終了... * /
そうでなければ、そのような特別に指定された場所の外に書かれたすべてのものは、プロジェクトの次世代の間にカブによって容赦なく書き換えられます。
まず、エコーを実行しましょう 。仮想COMポートに送信されたすべてのものを取得します。
USB経由でデータを受信すると、ファイル「 usbd_cdc_if.c 」のCDC_Receive_FSプロシージャ(uint8_t * Buf、uint32_t * Len)が呼び出されます。
受け取ったすべての返送を追加します。
エコー
static int8_t CDC_Receive_FS(uint8_t * Buf、uint32_t * Len)
{
/ *ユーザーコード開始6 * /
CDC_Transmit_FS(Buf、* Len); //受信したすべてを送信します
USBD_CDC_SetRxBuffer(hUsbDevice_0、&UserRxBufferFS [0]);
USBD_CDC_ReceivePacket(hUsbDevice_0); //次のパケットを有効にします
return(USBD_OK);
/ *ユーザーコードエンド6 * /
}
{
/ *ユーザーコード開始6 * /
CDC_Transmit_FS(Buf、* Len); //受信したすべてを送信します
USBD_CDC_SetRxBuffer(hUsbDevice_0、&UserRxBufferFS [0]);
USBD_CDC_ReceivePacket(hUsbDevice_0); //次のパケットを有効にします
return(USBD_OK);
/ *ユーザーコードエンド6 * /
}
コンパイル、ロードします。 ターミナルプログラムで仮想COMポートを開きます。 ポートパラメータ(速度、パリティ)は任意です。 エコーが機能することを確認します。
CANハッカーへの適応
次に、CAN Hackerプログラムを操作するためのプロトコルの実装を開始します。 プロトコル自体は、プロジェクトページのUSB <> CANバスインターフェイス(CAN Hacker)の「description」ファイルで表示するか、インターネットで「Lawicel Protokol」の名前を検索できます。
USBTraceプログラムは、アダプターの初期化プロセスを監視しています。
コマンド "version request"に応答する必要があります。他のすべての要求には、単に "OK"(0x0D)と応答します。
プロシージャCDC_Receive_FSを変更します
static int8_t CDC_Receive_FS(uint8_t * Buf、uint32_t * Len)
{
/ *ユーザーコード開始6 * /
uint32_t num_bytes;
uint8_t res;
uint8_t tmp_byte;
HAL_GPIO_TogglePin(GPIOC、GPIO_PIN_9);
スイッチ(Buf [0])
{
ケース「V」:
num_bytes = sprintf((char *)UserTxBufferFS、 "V0101 \ r");
休憩;
ケース 'v':
num_bytes = sprintf((char *)UserTxBufferFS、 "vSTM32 \ r");
休憩;
デフォルト:
num_bytes = sprintf((char *)UserTxBufferFS、 "\ r");
休憩;
}
USBD_CDC_SetTxBuffer(hUsbDevice_0、(uint8_t *)&UserTxBufferFS [0]、num_bytes);
USBD_CDC_TransmitPacket(hUsbDevice_0);
// CDC_Transmit_FS(Buf、* Len);
USBD_CDC_SetRxBuffer(hUsbDevice_0、&UserRxBufferFS [0]);
USBD_CDC_ReceivePacket(hUsbDevice_0);
return(USBD_OK);
/ *ユーザーコードエンド6 * /
}
{
/ *ユーザーコード開始6 * /
uint32_t num_bytes;
uint8_t res;
uint8_t tmp_byte;
HAL_GPIO_TogglePin(GPIOC、GPIO_PIN_9);
スイッチ(Buf [0])
{
ケース「V」:
num_bytes = sprintf((char *)UserTxBufferFS、 "V0101 \ r");
休憩;
ケース 'v':
num_bytes = sprintf((char *)UserTxBufferFS、 "vSTM32 \ r");
休憩;
デフォルト:
num_bytes = sprintf((char *)UserTxBufferFS、 "\ r");
休憩;
}
USBD_CDC_SetTxBuffer(hUsbDevice_0、(uint8_t *)&UserTxBufferFS [0]、num_bytes);
USBD_CDC_TransmitPacket(hUsbDevice_0);
// CDC_Transmit_FS(Buf、* Len);
USBD_CDC_SetRxBuffer(hUsbDevice_0、&UserRxBufferFS [0]);
USBD_CDC_ReceivePacket(hUsbDevice_0);
return(USBD_OK);
/ *ユーザーコードエンド6 * /
}
その後、CAN Hackerプログラムはアダプターを「見る」ことができます。
CANインターフェイスをプロジェクトに追加します
Cubeで、「Configuration」->「Peripheals」->「CAN1」チェックボックス「Master mode」を設定します。 「構成」タブ「CAN1」で、速度を設定し、受信時の割り込みを有効にします。
CANのボーレートは500 Kbpsに設定されています。
APB1バスからのCANモジュールのクロック周波数は36 MHzで、それを6で割ると、1クォンタムの時間が得られます。
1バイトは1 + 6 + 5クォンタムで送信され、36/6 /(1 + 6 + 5)= 0.5 MHz、つまり500 Kbpsになります。
「Prescaler(for Quantum)」分周器を変更することにより、125,250,500,1000 Kbpsの標準速度を取得できます。
1バイトは1 + 6 + 5クォンタムで送信され、36/6 /(1 + 6 + 5)= 0.5 MHz、つまり500 Kbpsになります。
「Prescaler(for Quantum)」分周器を変更することにより、125,250,500,1000 Kbpsの標準速度を取得できます。
Cubeでプロジェクトを生成し、IDEで開きます。
「main.c」で、CAN用のバッファーを追加し、着信メッセージフィルターを構成し、 HAL_CAN_RxCpltCallbackプロシージャを追加する必要があります。 この手順は、CAN割り込みから呼び出されます。 もちろん、名前はそうすることができます、なぜなら カブによって生成されたプロジェクトの「腸」で綴られているのはそれです。 CAN経由で受信したものはすべて、プロトコルに従ってUSBに送信されます。 たとえば、CANを介して、アドレス0x123から、8バイトのデータ0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88を受信した場合、これをUSBパッケージ「t12381122334455667788」にパックし、最後に文字0x0Dを追加して、PCの仮想COMポートに送信します。
CAN小包受付を追加
受信/送信バッファ
/ *ユーザーコード開始PV * /
static CanTxMsgTypeDef can1TxMessage;
static CanRxMsgTypeDef can1RxMessage;
/ *ユーザーコード終了PV * /
小包受付手順
/ *ユーザーコード開始0 * /
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef * CanHandle)
{
uint32_t num_bytes;
uint8_t buf [200];
num_bytes = sprintf((char *)buf、 "t%3.3X%1.1X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%4.4X \ r"、\
CanHandle-> pRxMsg-> StdId、\
CanHandle-> pRxMsg-> DLC、\
CanHandle-> pRxMsg->データ[0]、\
CanHandle-> pRxMsg->データ[1]、\
CanHandle-> pRxMsg->データ[2]、\
CanHandle-> pRxMsg->データ[3]、\
CanHandle-> pRxMsg->データ[4]、\
CanHandle-> pRxMsg->データ[5]、\
CanHandle-> pRxMsg->データ[6]、\
CanHandle-> pRxMsg->データ[7] \
);
CDC_Transmit_FS(buf、num_bytes); // CAN経由で受け取ったものをUSBに送信します
HAL_CAN_Receive_IT(&hcan1、CAN_FIFO0); //次のパッケージを待つ
}
/ *ユーザーコードエンド0 * /
外部関数CDC_Transmit_FSを使用するには、.hファイルを含めます
/ *ユーザーコードの開始に含まれるもの* /
#include "usbd_cdc_if.h"
/ *ユーザーコードの終了に含まれるもの* /
メインメインループで、バッファーの初期化を追加し、受信フィルターを構成します
/ *ユーザーコード開始2 * /
hcan1.pTxMsg =&can1TxMessage;
hcan1.pRxMsg =&can1RxMessage;
/ *ユーザーコードエンド2 * /
//フィルタを構成します-すべてのパッケージを受け取ります
CAN_FilterConfTypeDef canFilterConfig;
canFilterConfig.FilterNumber = 0;
canFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
canFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
canFilterConfig.FilterIdHigh = 0x0000;
canFilterConfig.FilterIdLow = 0x0000;
canFilterConfig.FilterMaskIdHigh = 0x0000 << 5;
canFilterConfig.FilterMaskIdLow = 0x0000;
canFilterConfig.FilterFIFOAssignment = 0;
canFilterConfig.FilterActivation = ENABLE;
canFilterConfig.BankNumber = 1;
HAL_CAN_ConfigFilter(&hcan1、&canFilterConfig);
HAL_CAN_Receive_IT(&hcan1、CAN_FIFO0); //パッケージの受け取りを許可します
/ *ユーザーコードエンド2 * /
さらにUSBバッファーのサイズを変更します
#define APP_RX_DATA_SIZE 1000 //は4
#define APP_TX_DATA_SIZE 1000 //は4でした
/ *ユーザーコード開始PV * /
static CanTxMsgTypeDef can1TxMessage;
static CanRxMsgTypeDef can1RxMessage;
/ *ユーザーコード終了PV * /
小包受付手順
/ *ユーザーコード開始0 * /
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef * CanHandle)
{
uint32_t num_bytes;
uint8_t buf [200];
num_bytes = sprintf((char *)buf、 "t%3.3X%1.1X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%4.4X \ r"、\
CanHandle-> pRxMsg-> StdId、\
CanHandle-> pRxMsg-> DLC、\
CanHandle-> pRxMsg->データ[0]、\
CanHandle-> pRxMsg->データ[1]、\
CanHandle-> pRxMsg->データ[2]、\
CanHandle-> pRxMsg->データ[3]、\
CanHandle-> pRxMsg->データ[4]、\
CanHandle-> pRxMsg->データ[5]、\
CanHandle-> pRxMsg->データ[6]、\
CanHandle-> pRxMsg->データ[7] \
);
CDC_Transmit_FS(buf、num_bytes); // CAN経由で受け取ったものをUSBに送信します
HAL_CAN_Receive_IT(&hcan1、CAN_FIFO0); //次のパッケージを待つ
}
/ *ユーザーコードエンド0 * /
外部関数CDC_Transmit_FSを使用するには、.hファイルを含めます
/ *ユーザーコードの開始に含まれるもの* /
#include "usbd_cdc_if.h"
/ *ユーザーコードの終了に含まれるもの* /
メインメインループで、バッファーの初期化を追加し、受信フィルターを構成します
/ *ユーザーコード開始2 * /
hcan1.pTxMsg =&can1TxMessage;
hcan1.pRxMsg =&can1RxMessage;
/ *ユーザーコードエンド2 * /
//フィルタを構成します-すべてのパッケージを受け取ります
CAN_FilterConfTypeDef canFilterConfig;
canFilterConfig.FilterNumber = 0;
canFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
canFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
canFilterConfig.FilterIdHigh = 0x0000;
canFilterConfig.FilterIdLow = 0x0000;
canFilterConfig.FilterMaskIdHigh = 0x0000 << 5;
canFilterConfig.FilterMaskIdLow = 0x0000;
canFilterConfig.FilterFIFOAssignment = 0;
canFilterConfig.FilterActivation = ENABLE;
canFilterConfig.BankNumber = 1;
HAL_CAN_ConfigFilter(&hcan1、&canFilterConfig);
HAL_CAN_Receive_IT(&hcan1、CAN_FIFO0); //パッケージの受け取りを許可します
/ *ユーザーコードエンド2 * /
さらにUSBバッファーのサイズを変更します
#define APP_RX_DATA_SIZE 1000 //は4
#define APP_TX_DATA_SIZE 1000 //は4でした
また、CANトランシーバにはスタンバイモードに切り替えるための入力があることを忘れないでください。
Cubeで、PB7を出口に割り当てます。 デフォルトでは、出力は0に設定されます。
コンパイル、ロード、チェックします。 原則として、機能します。 CAN HackerにはCANからのパッケージが表示されますが、CANパッケージ間の時間が1ミリ秒未満の場合、メッセージはスキップされ、1から送信されます。
例えば写真。 CANハッカーでは1ミリ秒ごとに2つの小包が到着します。
チャネル1-CAN RX、コントローラーに受け入れます
チャンネル2-CAN TX、コントローラーからの確認が見える
チャンネル3.4 -USB D +、D-
さらに、CANバスで集中的な交換がある場合、アダプターはUSB経由で検出されず、「不明なデバイス」が書き込みます。
最初にUSBを接続し、CAN Hackerを起動してから、CANを接続する必要があります。
CANパッケージを受信するためのリングバッファーを追加する
CANからUSBにパッケージを作成して送信している間に、CANの次のパッケージがすり抜けて処理されずに残っていることは明らかです。 したがって、CANパーセルを受信するときは、すべてをすばやくバッファーに入れ、別の場所のどこかにこのバッファーをレーキしてUSBに送信します。
バイト単位でバッファーに書き込むため、メモリ内の配列の終わりから始まりまでの「ループ」に従うのは簡単です。
USBD_CDC_SetTxBufferプロシージャとUSBD_CDC_TransmitPacketプロシージャを使用してバッファから送信し、送信するデータの開始とその量を示します。 設定時に「リングを作る」必要がある場合は、2つの段階で送信します。最初にメモリ内の配列の最後にデータを送信し、次に残りを送信します。
リングバッファの実装
USBを送信するためのバッファーは既に定義されています
uint8_t UserTxBufferFS [APP_TX_DATA_SIZE];
「usb_cdc_if.c」で、このバッファへのポインタを定義します
uint32_t ptrWriteUserTxBufferFS = 0; // CANから受信するときにバッファに書き込みます
uint32_t ptrReadUserTxBufferFS = 0; //バッファから読み取り、USBに送信
/ *ユーザーコードエンド1 * /
データをバッファに追加し、バッファから送信する手順を説明します
残念ながら、「usb_cdc_if.c」には通常の場所がなかったため、「USBD_CDC_Private_Macros」セクションに押し込む必要がありました。
/ *ユーザーコード開始2 * /
extern uint8_t UserRxBufferFS [APP_RX_DATA_SIZE];
extern uint8_t UserTxBufferFS [APP_TX_DATA_SIZE];
extern uint8_t interface_state;
extern USBD_HandleTypeDef * hUsbDevice_0;
// ------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------
uint8_t CDC_add_buf_to_transmit(uint8_t * Buf、uint16_t Len)//送信用のバッファーに追加
{
uint16_t _cnt = Len;
while(_cnt)
{
UserTxBufferFS [ptrWriteUserTxBufferFS] = * Buf;
ptrWriteUserTxBufferFS ++;
Buf ++;
ptrWriteUserTxBufferFS%= APP_TX_DATA_SIZE; //ループ
_cnt--;
}
return(0);
}
// ------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------
uint8_t CDC_periodic_callback(void)//転送するデータがあるかどうかを定期的に確認します
{
uint32_t buffptr;
uint32_t buffsize;
if(ptrReadUserTxBufferFS!= ptrWriteUserTxBufferFS)
{
__disable_irq(); //集中的なCAN交換により、これなしではすべてが「バラバラ」になります
if(ptrReadUserTxBufferFS> ptrWriteUserTxBufferFS)//リングを作成しますか?
{
buffsize = APP_TX_DATA_SIZE-ptrReadUserTxBufferFS; //ステージ1、バイトから配列の最後まで
}
他に
{
buffsize = ptrWriteUserTxBufferFS-ptrReadUserTxBufferFS; //すべてのデータを一度に
}
__enable_irq();
buffptr = ptrReadUserTxBufferFS;
if(interface_state!= 1)return(1); //インターフェイスが設定されていない場合、送信しません
USBD_CDC_SetTxBuffer(hUsbDevice_0、(uint8_t *)&UserTxBufferFS [buffptr]、buffsize);
if(USBD_CDC_TransmitPacket(hUsbDevice_0)== USBD_OK)
{
ptrReadUserTxBufferFS + = buffsize;
if(ptrReadUserTxBufferFS == APP_TX_DATA_SIZE)
{
ptrReadUserTxBufferFS = 0; //ループ
}
}
}
return(0);
}
/ *ユーザーコードエンド2 * /
「main.c」に変数を追加します
uint8_t interface_state = 0;
「O」コマンドがCAN Hackerから来るまで-チューニングモードから動作モードに切り替えても、USBには何も送信されません。 インターフェイスはまだ構成されていないことを考慮します
CAN割り込みでUSBへの直接送信を送信バッファーに追加して置き換えます
CDC_add_buf_to_transmitへのCDC_Transmit_FS
送信するバッファの定期的なポーリングを追加します
(1)
{
/ *ユーザーコードの終了中* /
/ *ユーザーコード開始3 * /
CDC_periodic_callback();
}
/ *ユーザーコードエンド3 * /
uint8_t UserTxBufferFS [APP_TX_DATA_SIZE];
「usb_cdc_if.c」で、このバッファへのポインタを定義します
uint32_t ptrWriteUserTxBufferFS = 0; // CANから受信するときにバッファに書き込みます
uint32_t ptrReadUserTxBufferFS = 0; //バッファから読み取り、USBに送信
/ *ユーザーコードエンド1 * /
データをバッファに追加し、バッファから送信する手順を説明します
残念ながら、「usb_cdc_if.c」には通常の場所がなかったため、「USBD_CDC_Private_Macros」セクションに押し込む必要がありました。
/ *ユーザーコード開始2 * /
extern uint8_t UserRxBufferFS [APP_RX_DATA_SIZE];
extern uint8_t UserTxBufferFS [APP_TX_DATA_SIZE];
extern uint8_t interface_state;
extern USBD_HandleTypeDef * hUsbDevice_0;
// ------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------
uint8_t CDC_add_buf_to_transmit(uint8_t * Buf、uint16_t Len)//送信用のバッファーに追加
{
uint16_t _cnt = Len;
while(_cnt)
{
UserTxBufferFS [ptrWriteUserTxBufferFS] = * Buf;
ptrWriteUserTxBufferFS ++;
Buf ++;
ptrWriteUserTxBufferFS%= APP_TX_DATA_SIZE; //ループ
_cnt--;
}
return(0);
}
// ------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------
uint8_t CDC_periodic_callback(void)//転送するデータがあるかどうかを定期的に確認します
{
uint32_t buffptr;
uint32_t buffsize;
if(ptrReadUserTxBufferFS!= ptrWriteUserTxBufferFS)
{
__disable_irq(); //集中的なCAN交換により、これなしではすべてが「バラバラ」になります
if(ptrReadUserTxBufferFS> ptrWriteUserTxBufferFS)//リングを作成しますか?
{
buffsize = APP_TX_DATA_SIZE-ptrReadUserTxBufferFS; //ステージ1、バイトから配列の最後まで
}
他に
{
buffsize = ptrWriteUserTxBufferFS-ptrReadUserTxBufferFS; //すべてのデータを一度に
}
__enable_irq();
buffptr = ptrReadUserTxBufferFS;
if(interface_state!= 1)return(1); //インターフェイスが設定されていない場合、送信しません
USBD_CDC_SetTxBuffer(hUsbDevice_0、(uint8_t *)&UserTxBufferFS [buffptr]、buffsize);
if(USBD_CDC_TransmitPacket(hUsbDevice_0)== USBD_OK)
{
ptrReadUserTxBufferFS + = buffsize;
if(ptrReadUserTxBufferFS == APP_TX_DATA_SIZE)
{
ptrReadUserTxBufferFS = 0; //ループ
}
}
}
return(0);
}
/ *ユーザーコードエンド2 * /
「main.c」に変数を追加します
uint8_t interface_state = 0;
「O」コマンドがCAN Hackerから来るまで-チューニングモードから動作モードに切り替えても、USBには何も送信されません。 インターフェイスはまだ構成されていないことを考慮します
CAN割り込みでUSBへの直接送信を送信バッファーに追加して置き換えます
CDC_add_buf_to_transmitへのCDC_Transmit_FS
送信するバッファの定期的なポーリングを追加します
(1)
{
/ *ユーザーコードの終了中* /
/ *ユーザーコード開始3 * /
CDC_periodic_callback();
}
/ *ユーザーコードエンド3 * /
コンパイル、ロードします。 CAN Hackerですべてのメッセージが隙間なく表示されるようになりました。
タイムスタンプを追加
CAN Hackerプロトコルは、各メッセージに「タイムスタンプ」を提供します。 値の範囲は0..60000ミリ秒です。
これにはTIM1を使用します。
Cubの[構成]-> [ペリフェラル]-> [TIM1]で、[クロックソース] = [内部クロック]を選択します。
タイマーのクロック周波数を1000 Hz(1ms)に設定します。 APB2のクロック周波数を下げる必要があります。
私たちは、速度の点で私たちにとって重要なものはAPB2から供給されないと確信しています。
「リファレンスマニュアル」から「STM32F1」へ:
timer1、I / Oポート、ADC、SPI、USARTがAPB2からクロックされることがわかり、APB2の周波数を安全に下げることができます。
Cubの[クロック構成]タブで、[APB2プリスケーラー]を8に設定し、18 MHzタイマーのクロック周波数を取得します。
「構成」タブで「TIM1」を設定します
プリスケーラ(PSC-16ビット値)= 18000
カウンター周期(AutoReloadレジスタ)= 60000
コードを生成し、IDEで開きます。
メインにタイマー開始を追加します
/ *ユーザーコード開始2 * /
HAL_TIM_Base_Start(&htim1);
HAL_TIM_Base_Start(&htim1);
CANからUSBへのメッセージにタイムスタンプを追加します
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef * CanHandle)
{
uint32_t num_bytes;
uint8_t buf [200];
静的uint32_t時間;
time = __HAL_TIM_GetCounter(&htim1);
num_bytes = sprintf((char *)buf、 "t%3.3X%1.1X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%4.4X \ r"、\
CanHandle-> pRxMsg-> StdId、\
CanHandle-> pRxMsg-> DLC、\
CanHandle-> pRxMsg->データ[0]、\
CanHandle-> pRxMsg->データ[1]、\
CanHandle-> pRxMsg->データ[2]、\
CanHandle-> pRxMsg->データ[3]、\
CanHandle-> pRxMsg->データ[4]、\
CanHandle-> pRxMsg->データ[5]、\
CanHandle-> pRxMsg->データ[6]、\
CanHandle-> pRxMsg->データ[7]、\
時間
);
if(interface_state == 1)CDC_add_buf_to_transmit(buf、num_bytes);
HAL_CAN_Receive_IT(&hcan1、CAN_FIFO0);
}
{
uint32_t num_bytes;
uint8_t buf [200];
静的uint32_t時間;
time = __HAL_TIM_GetCounter(&htim1);
num_bytes = sprintf((char *)buf、 "t%3.3X%1.1X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%4.4X \ r"、\
CanHandle-> pRxMsg-> StdId、\
CanHandle-> pRxMsg-> DLC、\
CanHandle-> pRxMsg->データ[0]、\
CanHandle-> pRxMsg->データ[1]、\
CanHandle-> pRxMsg->データ[2]、\
CanHandle-> pRxMsg->データ[3]、\
CanHandle-> pRxMsg->データ[4]、\
CanHandle-> pRxMsg->データ[5]、\
CanHandle-> pRxMsg->データ[6]、\
CanHandle-> pRxMsg->データ[7]、\
時間
);
if(interface_state == 1)CDC_add_buf_to_transmit(buf、num_bytes);
HAL_CAN_Receive_IT(&hcan1、CAN_FIFO0);
}
繰り返しになりますが、CANでの集中的な交換により、USB交換は「終了」します。
今回、CANを中断するのに長い時間がかかるsprintfルーチンが原因です。
sprintfを使用せずに、パッケージの構成をCANからUSBに書き換えます。
4ビットを16進ASCII文字に、またはその逆に変換する手順
uint8_t halfbyte_to_hexascii(uint8_t _halfbyte)
{
_halfbyte&= 0x0F;
if(_halfbyte> = 10)return( 'A' + _halfbyte-10);
そうでなければreturn( '0' + _halfbyte);
}
uint8_t hexascii_to_halfbyte(uint8_t _ascii)
{
if((_ ascii> = '0')&&(_ascii <= '9'))return(_ascii-'0');
if((_ ascii> = 'a')&&(_ascii <= 'f'))return(_ascii-'a');
if((_ ascii> = 'A')&&(_ascii <= 'F'))return(_ascii-'A');
return(0xFF);
}
{
_halfbyte&= 0x0F;
if(_halfbyte> = 10)return( 'A' + _halfbyte-10);
そうでなければreturn( '0' + _halfbyte);
}
uint8_t hexascii_to_halfbyte(uint8_t _ascii)
{
if((_ ascii> = '0')&&(_ascii <= '9'))return(_ascii-'0');
if((_ ascii> = 'a')&&(_ascii <= 'f'))return(_ascii-'a');
if((_ ascii> = 'A')&&(_ascii <= 'F'))return(_ascii-'A');
return(0xFF);
}
プロシージャHAL_CAN_RxCpltCallbackを変更します
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef * CanHandle)
{
uint32_t num_bytes;
uint8_t buf [200];
静的uint32_t時間;
time = __HAL_TIM_GetCounter(&htim1);
/ *
num_bytes = sprintf((char *)buf、 "t%3.3X%1.1X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%4.4X \ r"、\
CanHandle-> pRxMsg-> StdId、\
CanHandle-> pRxMsg-> DLC、\
CanHandle-> pRxMsg->データ[0]、\
CanHandle-> pRxMsg->データ[1]、\
CanHandle-> pRxMsg->データ[2]、\
CanHandle-> pRxMsg->データ[3]、\
CanHandle-> pRxMsg->データ[4]、\
CanHandle-> pRxMsg->データ[5]、\
CanHandle-> pRxMsg->データ[6]、\
CanHandle-> pRxMsg->データ[7]、\
時間
);
* /
num_bytes = 0;
buf [num_bytes ++] = 't';
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> StdId)>> 8);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> StdId)>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> StdId));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> DLC));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [0])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [0]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [1])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [1]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [2])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [2]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [3])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [3]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [4])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [4]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [5])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [5]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [6])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [6]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [7])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [7]));
buf [num_bytes ++] = halfbyte_to_hexascii((time)>> 12);
buf [num_bytes ++] = halfbyte_to_hexascii((time)>> 8);
buf [num_bytes ++] = halfbyte_to_hexascii((time)>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((time)>> 0);
buf [num_bytes ++] = '\ r';
if(interface_state == 1)CDC_add_buf_to_transmit(buf、num_bytes);
HAL_CAN_Receive_IT(&hcan1、CAN_FIFO0);
}
{
uint32_t num_bytes;
uint8_t buf [200];
静的uint32_t時間;
time = __HAL_TIM_GetCounter(&htim1);
/ *
num_bytes = sprintf((char *)buf、 "t%3.3X%1.1X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%4.4X \ r"、\
CanHandle-> pRxMsg-> StdId、\
CanHandle-> pRxMsg-> DLC、\
CanHandle-> pRxMsg->データ[0]、\
CanHandle-> pRxMsg->データ[1]、\
CanHandle-> pRxMsg->データ[2]、\
CanHandle-> pRxMsg->データ[3]、\
CanHandle-> pRxMsg->データ[4]、\
CanHandle-> pRxMsg->データ[5]、\
CanHandle-> pRxMsg->データ[6]、\
CanHandle-> pRxMsg->データ[7]、\
時間
);
* /
num_bytes = 0;
buf [num_bytes ++] = 't';
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> StdId)>> 8);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> StdId)>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> StdId));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> DLC));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [0])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [0]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [1])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [1]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [2])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [2]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [3])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [3]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [4])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [4]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [5])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [5]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [6])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [6]));
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [7])>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((CanHandle-> pRxMsg-> Data [7]));
buf [num_bytes ++] = halfbyte_to_hexascii((time)>> 12);
buf [num_bytes ++] = halfbyte_to_hexascii((time)>> 8);
buf [num_bytes ++] = halfbyte_to_hexascii((time)>> 4);
buf [num_bytes ++] = halfbyte_to_hexascii((time)>> 0);
buf [num_bytes ++] = '\ r';
if(interface_state == 1)CDC_add_buf_to_transmit(buf、num_bytes);
HAL_CAN_Receive_IT(&hcan1、CAN_FIFO0);
}
受信パフォーマンスに関するいくつかの実験:CANでは、アドレス0x321から1,000,000個のパッケージを生成し、CAN Hackerでは、受け入れられるパッケージの数を確認します。
速度500 Kbit / s、中断なしの送信、0.2%の損失:
速度1 Mbit / s、中断なしの送信、50%の損失:
速度1 Mbit / s、1 msごとに2つの区画、損失0%:
私の意見では良い結果です。
CANにメッセージを送信する機能を追加します
ファイル「usbd_cdc_if.c」のUSB CDC_Receive_FSプロシージャで、次を追加します。
CANへの送信
コンパイル、ダウンロード、検証、動作します。
ケース 't':
i = 1;
hcan1.pTxMsg-> StdId = hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg-> StdId =(hcan1.pTxMsg-> StdId << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg-> StdId =(hcan1.pTxMsg-> StdId << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg-> DLC = hexascii_to_halfbyte(Buf [i ++]);
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[0] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[1] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[2] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[3] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[4] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[5] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[6] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[7] = tmp_byte;
HAL_CAN_Transmit(&hcan1、10);
num_bytes = sprintf((char *)UserTxBufferFS、 "\ r");
休憩;
i = 1;
hcan1.pTxMsg-> StdId = hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg-> StdId =(hcan1.pTxMsg-> StdId << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg-> StdId =(hcan1.pTxMsg-> StdId << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg-> DLC = hexascii_to_halfbyte(Buf [i ++]);
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[0] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[1] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[2] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[3] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[4] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[5] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[6] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf [i ++]); tmp_byte =(tmp_byte << 4)+ hexascii_to_halfbyte(Buf [i ++]);
hcan1.pTxMsg->データ[7] = tmp_byte;
HAL_CAN_Transmit(&hcan1、10);
num_bytes = sprintf((char *)UserTxBufferFS、 "\ r");
休憩;
おわりに
これはおそらく停止する可能性があります。 「自分でやる」という見出しが呼ばれます。 誰もが望むなら、彼は独立して異なるCAN速度のサポートを追加し、29ビット拡張識別子、メッセージフィルター、リモートフレームを操作できます。
ASCIIチームで働くという原則が好きだったと言いたいです。 将来的には、USB-SPI、USB-I2C機能を実装する予定です。 たとえば、仮想COMポートを115200ボーに設定します-CANで動作し、57600で動作します-I2Cで動作し、9600で動作します-SPIで動作します。 もちろん、SPIまたはI2Cを使用する場合、CANハッカーは使用できなくなり、何らかのプロトコルを作成する必要があります。
この記事の完成したプロジェクトは、 ここからダウンロードできます 。
使用材料
1. USB <> CANバスインターフェイス(CANハッカー)
2. STM32-CAN-Busadapter
3. STM32VLDISCOVERYの説明と概略図
4. STM32F105リファレンスマニュアル