STM32 USB経由によるシリアルの送受

 BluePillを計測器として利用することを想定するとPCからのコマンドを受け付けた時に、機器の状態をPCに返すというような使い方を想定

USBポートを介してのシリアルのやりとり。中華ST-LINK利用時デバッグできない問題を回避するために使うというようなページは多数あるので参考に。

1.ピン設定

Connectivityの下のUSBを選択。窓の上のDvice(FS)にチェックをいれる。その他はデフォルトでOK。

次に、MiddlewareのUSB_DEVICEを選択して
Class for FSIPをCommunication Device Class(Virtual Port Com)に変更

2.Clockの設定

System Coreの下にあるRCCを選択。High Speed Clock (HSC)をCrystal/Ceramic Resonatorを選択。
その後、上のタブでClock Configrationを選択して、クロックを設定(一例は以下)
USBのクロックが48MHzになるように調整し、APB1のクロックが36MHzになるように設定をする。

ここまでできたらセーブして、コードを生成させる。

3.コーディング
書き込み(マイコン→PC)は簡単なので詳細は割愛
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
char cdcBuffer[40];
/* USER CODE END PD */
cdcBufferとか適当に宣言しておいて、

sprintf(cdcBuffer, "ch%d=%4f\n\r",1,Vtemp);
while(CDC_Transmit_FS((uint8_t*)cdcBuffer, strlen(cdcBuffer)) == USBD_OK) {}
とすればOk。(ここではVtempはfloatの変数を想定)

PCから文字列を受け取る方がちょっと複雑
main.c内にUSBからのやり取りのためのバッファーを定義

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
char USB_TX_buffer[64];
uint8_t USB_RX_buffer[64];
/* USER CODE END 0 */

その後、usbd_cdc_if.cを探してきて、その中を開き、
/* USER CODE BEGIN PRIVATE_DEFINES */
extern uint8_t USB_RX_buffer[64];
/* USER CODE END PRIVATE_DEFINES */

外部参照できるようにする(PC→マイコンをRXとしている)
さらに、下の方にあるCDC_Receive_FSの内容に追記を行う。

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);

  memset (USB_RX_buffer, '\0', 64);  // clear the buffer
  uint8_t len = (uint8_t)*Len;
  memcpy(USB_RX_buffer, Buf, len);  // copy the data to the buffer
  memset(Buf, '\0', len);   // clear the Buf also

  return (USBD_OK);
  /* USER CODE END 6 */
}

これで、文字列がUSB経由で飛び込んでくるとUSB_RX_bufferに格納されるようになる。
ここまでが準備で、mainに例として以下を記載する。

while (1)
  {
  /*Serial response from PC via USB*/
  char RESP_TEST1[]               = "test1\r\n";
  char RESP_TEST2[]               = "test2\r\n";
  char RESP_TEST3[]               = "test3\r\n";
  char RESP_TEST4[]               = "test4\r\n";

  //CDC_Receive_FS((uint8_t*)USB_RX_buffer, strlen(USB_RX_buffer);

  if (strcmp((const char *)USB_RX_buffer, RESP_TEST1) == 0)
  {
  sprintf(USB_TX_buffer, "test1 received\n\r");// handle OK
    while(CDC_Transmit_FS((uint8_t*)USB_TX_buffer, strlen(USB_TX_buffer)) == USBD_OK) {}

  }
  else if (strcmp((const char *)USB_RX_buffer, RESP_TEST2) == 0)
  {
  sprintf(USB_TX_buffer, "test2 received\n\r");// handle OK // handle ERROR
    while(CDC_Transmit_FS((uint8_t*)USB_TX_buffer, strlen(USB_TX_buffer)) == USBD_OK) {}

  }
  else if (strcmp((const char *)USB_RX_buffer, RESP_TEST3) == 0)
  {
  sprintf(USB_TX_buffer, "test3 received\n\r");// handle OK // handle FAIL
    while(CDC_Transmit_FS((uint8_t*)USB_TX_buffer, strlen(USB_TX_buffer)) == USBD_OK) {}
  }
  else if (strcmp((const char *)USB_RX_buffer, RESP_TEST4) == 0)
  {
  sprintf(USB_TX_buffer, "test4 received\n\r");// handle OK // handle FAIL
    while(CDC_Transmit_FS((uint8_t*)USB_TX_buffer, strlen(USB_TX_buffer)) == USBD_OK) {}
  }
  else
  {
  //sprintf(cdcBuffer, "");// handle OK // handle FAIL
  }

  memset(USB_RX_buffer, '\0', strlen(USB_RX_buffer));   // clear the USB_RX_buffer also
  HAL_Delay(1);
    /* USER CODE END WHILE */

ターミナルから、指定した文字列が入ってくると、それに対して応答するという例になっている。
マイコン側では高速に頻度高く例えば温度を計測し、それを一定間隔でエクセルに落とすってような時にと用意。



コメント

このブログの人気の投稿

Attiny85とAQM0802A(LCD)のI2C接続

Attiny85 FuseRest

HS101 STM32の自作お手軽オシロスコープ