Nucleo-F303K8でCAN Bus通信を行う

目的
---
Nucleo F303K8ボードでCAN Busによる通信を行う際の覚書を記載します。

環境
---

* Nucleo-F303K8 CAN通信環境

| 項目 | 内容 |
| --- | ---|
| OS | Windows 10 Home |
| マイコン | Nucleo-F303K8 |
| CANトランシーバー | MCP2551 [amazonリンク](!https://www.amazon.co.jp/MCP2551-TRANSCEIVER-HI-SPD-8-PDIP-MCP2551/dp/B07FR2GB6M/ref=sr_1_1?s=industrial&ie=UTF8&qid=1538238646&sr=1-1&keywords=mcp2551) |
| 終端抵抗 | 金属皮膜抵抗 120オーム 2個 |
| Nucleo Board Firmware | STM32Cube_FW_F3_V1.9.1 |


**CAN出力機能付きのNucleo Boardを利用する際はMCP2551素子を利用します, MCP2515と紛らわしいので注意してください(MCP2515はSPI通信をCAN通信に変換してくれます。)**

**Firmware V1.10からCAN通信ドライバの関数が変更になっています** [参考情報](!https://community.st.com/s/question/0D50X00009Xkg2MSAR/stm32f4xx-rework-of-hal-can-driver-examples) 
**新しいCAN通信ドライバのドキュメントはネットにあまり上がっていないので、詳細は次のフォルダ内にあるCAN通信の例を参考にするしか
なさそうです。**

**C:\Users\(ユーザー名)\STM32Cube\Repository\STM32Cube_FW_F3_V(Version番号)\Projects\STM32303C_EVAL\Examples\CAN**


* デバッグ用CAN bus受信環境

| 項目 | 内容 |
| --- | ----|
| マイコン (デバッグ用) | Arduino Uno R3 |
| Arduino CAN Bus Shield | Sparkfun CAN bus shiled [Sparkfunリンク](!https://www.sparkfun.com/products/13262) |

システム構成図
---

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253656/975a1773-4bc1-6002-b02a-ad0ba387c640.png)

配線例
---

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253656/896ed1ce-1cd7-d321-a9e2-b93d183f3bd6.png)



手順概要
---
* STM32CubeMX設定
* eclipseコード変更
* Arduino環境構築
* 動作確認

STM32CubeMX設定
---

#### Pinoutタブ

* RCC->High Speed Clock (HSE)をBYPASS Clock Sourceに設定 [参考情報](!https://community.st.com/s/question/0D50X00009XkWCJSA3/can-bus-cubemx-settings-and-transmit-f091cc)

* CAN->Master Modeに設定

* USART1はRS232通信が必要であれば設定する。(オプション)

**RCC->High Speed ClockでBYPASS Clock Sourceに設定した際には、initialise_monitor_handlesが見つかりませんエラーに
なってしまう。CAN Bus通信とsemihostingは両立できない？？。現在、原因究明中**

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253656/37601148-8935-5c8b-8e78-8f5b946640a2.png)

#### Clockタブ
以下のように設定しました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/253656/ce18e9fc-02f7-fd16-b110-07d1fba17b87.png)

#### Configurationタブ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253656/d4cc85f0-9573-2bdc-3c33-8f762415b4e6.png)

ボーレートは500kbpsで設定しています。[参考資料](!https://vector.com/portal/medien/cmc/application_notes/AN-AND-1-106_Basic_CAN_Bit_Timing.pdf)

計算式は以下です, 

$ CAN Bus Clock = APB1 Periferal Clock (32MHz) / Prescaler (4) = 8 MHz $

ボーレートは500kbpsにしたいので

$ Time Quantum = 8 [MHz] / 0.5 [Mbps] = 16 [Times]$

**つまり、1bit送る際に16回クロックの立ち上がりが来る**

Start bitで1 [Times] 消費するので、Bit Segment 1を11 [Times]に設定すると

$ Sample Point = (11 + 1) [Times] / 16 [Times] = 0.75 $

つまり、この例ではSample Pointは75%。(ただ、添付資料によるとSample Pointは80%から90%に設定するのがいいらしいが、配線が短ければ50%~70%でも動作する)

上記設定が終われば、Generate Projectでプロジェクトを生成します。


eclipseコード修正 (can送信テスト)
---

* Main関数while loop内

```c
/* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1)
	{

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
		hcan.pTxMsg->Data[0] = 0x31;
		hcan.pTxMsg->Data[1] = 0x32;
		if (HAL_CAN_Transmit(&hcan, 10) != HAL_OK) {
			Error_Handler();
		}
		HAL_Delay(1000);
	}
  /* USER CODE END 3 */
```

* MX_CAN_Init内

```c
/* CAN init function */
static void MX_CAN_Init(void)
{

  hcan.Instance = CAN;
  hcan.Init.Prescaler = 4;
  hcan.Init.Mode = CAN_MODE_NORMAL;
  hcan.Init.SJW = CAN_SJW_1TQ;
  hcan.Init.BS1 = CAN_BS1_11TQ;
  hcan.Init.BS2 = CAN_BS2_4TQ;
  hcan.Init.TTCM = DISABLE;
  hcan.Init.ABOM = DISABLE;
  hcan.Init.AWUM = DISABLE;
  hcan.Init.NART = DISABLE;
  hcan.Init.RFLM = DISABLE;
  hcan.Init.TXFP = DISABLE;
  if (HAL_CAN_Init(&hcan) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  CAN_FilterConfTypeDef  sFilterConfig;
  static CanTxMsgTypeDef        TxMessage;
  static CanRxMsgTypeDef        RxMessage;
  hcan.pTxMsg = &TxMessage;
  hcan.pRxMsg = &RxMessage;
  hcan.pTxMsg = &TxMessage;
  hcan.pRxMsg = &RxMessage;

  /*##-2- Configure the CAN Filter ###########################################*/
  sFilterConfig.FilterNumber = 0;
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = 0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;

  if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
  {
    /* Filter configuration Error */
    Error_Handler();
  }

  /*##-3- Configure Transmission process #####################################*/
  hcan.pTxMsg->StdId = 0x321;
  hcan.pTxMsg->RTR = CAN_RTR_DATA;
  hcan.pTxMsg->IDE = CAN_ID_STD;
  hcan.pTxMsg->DLC = 2;

}
```


Arduino環境構築
---

* Sparkfun社のCAN-Bus-Libraryを[ダウンロード](!https://github.com/sparkfun/SparkFun_CAN-Bus_Arduino_Library)

* "C:\Program Files (x86)\Arduino\libraries"以下に展開したフォルダ内の"\Libraries\Canbus"をコピーします。

* 以下のコードをArduino IDEにコピーしてArduinoに書き込みます。

```c
/****************************************************************************
CAN Read Demo for the SparkFun CAN Bus Shield. 

Written by Stephen McCoy. 
Original tutorial available here: http://www.instructables.com/id/CAN-Bus-Sniffing-and-Broadcasting-with-Arduino
Used with permission 2016. License CC By SA. 

Distributed as-is; no warranty is given.
*************************************************************************/

#include <Canbus.h>
#include <defaults.h>
#include <global.h>
#include <mcp2515.h>
#include <mcp2515_defs.h>

//********************************Setup Loop*********************************//

void setup() {
  Serial.begin(9600); // For debug use
  Serial.println("CAN Read - Testing receival of CAN Bus message");  
  delay(1000);
  
  if(Canbus.init(CANSPEED_500))  //Initialise MCP2515 CAN controller at the specified speed
    Serial.println("CAN Init ok");
  else
    Serial.println("Can't init CAN");
    
  delay(1000);
}

//********************************Main Loop*********************************//

void loop(){

  tCAN message;
if (mcp2515_check_message()) 
	{
   Serial.println("Getting Message");
    if (mcp2515_get_message(&message)) 
	{
        //if(message.id == 0x620 and message.data[2] == 0xFF)  //uncomment when you want to filter
             //{
               
               Serial.print("ID: ");
               Serial.print(message.id,HEX);
               Serial.print(", ");
               Serial.print("Data: ");
               Serial.print(message.header.length,DEC);
               for(int i=0;i<message.header.length;i++) 
                {	
                  Serial.print(message.data[i],HEX);
                  Serial.print(" ");
                }
               Serial.println("");
             //}
           }}

}
```

動作確認
---

* eclipseを立ち上げてNucleo-F303K8のプログラムを書き込んで実行します。

* Arduinoでシリアルモニタを立ち上げて以下のような表示が出ると成功です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253656/5b578827-3322-0285-7ca8-da055848b655.png)