STM32F4を弄る(5) PCとシリアル通信 受信編

STM32

こん○○は、よふかしわーくすの、よふかしさんです
5回目はBlack Pillと呼ばれるWeAct製かそれ系のSTM32F411を使って
Cube環境でPCとのシリアル通信 受信編をやってみます

特に今回はSTM32F411に搭載されているUSB機能と、
Black Pillに搭載されているUSB Type-Cポートを使用、それ以外のデバイスを使用しないで
VCP(Virtual COM Port)で通信してみたいと思います

今回使用している開発環境は、
CubeMX:Version 6.13.0
CubeIDE:Version 1.17.0
で解説していきます

シリアル受信の動作確認方法の検討

LチカはLEDを見れば動作がわかりますし、
PCへのシリアル送信は、TeraTermの画面上に任意の文字列を表示させることで動作確認できました
マイコンでのシリアル受信の場合、受信したことを簡単に示すものが何もないので
(ArduinoだとRxのLEDが光ったりしますが、Black Pillにはない)
何らか目視でわかる動作をする様に設計していきたいと思います

といっても、まだまだBlack Pill単体で使っていきたいので、TeraTermとLEDだけで実現しよう、
ということで、今回は下記の仕様でやっていきたいと思います

  • TeraTermからマイコンに文字列を送信する
  • マイコンで受信した文字列が”LED ON\n”の場合、LEDを点灯する
  • マイコンで受信した文字列が”LED OFF\n”の場合、LEDを消灯する
  • 受信した文字列はPCにそのまま送信し返す

さらに前回からの続きとしては、

  • 0.5s間隔のLチカはやめる
  • 05s間隔のシリアル送信はやめる

とします

CubeIDEでシリアル受信コードを書いていく

まずは、前回までの処理をコメントアウトします

  /* USER CODE BEGIN 2 */

//  char Tx_Buf[] = "Hello World\r\n";

  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
//	HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
//
//	CDC_Transmit_FS(Tx_Buf, strlen(Tx_Buf));

	HAL_Delay(500);
  }
  /* USER CODE END 3 */

今回は編集ファイルをミニマムにするために、usbd_cdc_if.cのみを編集していくことにします
まずは、受信バッファのサイズをdefine定義

/* USER CODE BEGIN PRIVATE_DEFINES */

#define RX_BUFFER_SIZE (64)

/* USER CODE END PRIVATE_DEFINES */

仕様に基づくオリジナルコードを追加

/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */

static void RxCommand(uint8_t* Buf, uint32_t *Len);
static void ProcessCommand(uint8_t *command);

/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */

static void RxCommand(uint8_t* Buf, uint32_t *Len)
{
	uint8_t received_char;

	static uint8_t USB_CommandBuffer[RX_BUFFER_SIZE + 1]; // 受信コマンドバッファ + ヌル文字
	static uint32_t USB_CommandLength = 0;                // 現在のコマンド長

	for (uint32_t i = 0; i < *Len; i++)
	{
		received_char = Buf[i];

		// 改行コード(\n または \r)でコマンドを判定
		if (received_char == '\n' || received_char == '\r')
		{
			// 受信データをコマンドとして処理
			USB_CommandBuffer[USB_CommandLength] = '\0'; // ヌル文字追加
			ProcessCommand(USB_CommandBuffer);

			// コマンドバッファをリセット
			USB_CommandLength = 0;
		}
		else
		{
			// バッファにデータを追加(オーバーフロー防止)
			if (USB_CommandLength < RX_BUFFER_SIZE)
			{
				USB_CommandBuffer[USB_CommandLength++] = received_char;
			}
		}
	}

	CDC_Transmit_FS(Buf, *Len); // 受信データをそのままPCに返す
}

static void ProcessCommand(uint8_t *command)
{
    if (strcmp((char *)command, "LED ON") == 0)
    {
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); // LED ON
    }
    else if (strcmp((char *)command, "LED OFF") == 0)
    {
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);   // LED OFF
    }
    else
    {
        // 何もしない
    }
}

USB VCPの受信関数にオリジナル関数のコールを追加

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */

  RxCommand(Buf, Len); // 追加

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

解説

USB VCPは最大のバッファが64Byteなので、それに従ってバッファサイズは64Byteにしています
RxCommand関数では基本的に、
USB VCPで受信したデータを1ByteずつUSB_CommandBufferに溜めていくのと同時に、
CDC_Transmit_FS関数を使用して受信データをそのままPCに返していきます

改行コードを受信したらUSB_CommandBufferには改行コードを入れずにヌル文字を入れます

ProcessCommand関数では受信文字列に応じて、LEDをON/OFFします
ここでは、strcmp関数を使って文字列を判定しているので、先の通りヌル文字を追加しています

TeraTermの設定→端末を開いて、改行コードの受信、送信ともにLFに変更しておきます
(CRだと行頭に戻ってしまうので、表示上も改行させるためにLFにする)

TeraTermに”LED OFF”+Enterキー、”LED ON\n”+Enterキーを入力して、LEDがOFF/ONすること、
TeraTermで入力した文字が、そのままTeraTermに表示されれば成功です

終わりに

ひとまず、Black Pill単体でできる、PCとのシリアル通信 受信編でした
次回は実装系ではなく、Config等のTipsを書いておきたいと思います

公開日時:2024/12/23 0:30:28

コメント

タイトルとURLをコピーしました