FreeRTOS＆STM32CubeMXで生成されたUSB仮想シリアルポート（VPC）の利用例

FreeRTOS上でSTM32CubeMXを利用したUSB仮想シリアルポート利用コードの記録です。

ターゲットマイコン：[STM32L152RE](https://www.st.com/ja/microcontrollers-microprocessors/stm32l152re.html "STM32L152RE")

一般的サンプルをアレンジした参考ソースコードとなります。
元ソースコードの生成方法や、一部の関数、変数等については説明を割愛いたします。

特記事項：
　内部送信バッファがオーバーフローすると送信が停止するため、
　送信残データはタイマー処理等でUSBCOM_rewrite()を呼び出し吐き出す工夫が必要です。


【注意事項】
本記事中のソフトウェアはサンプルコードであり、ソフトウェアを使用した結果いかなる損害等が発生しても当方は一切責任を負いません。

●ソースコード

```ruby:usb_com.c
/**
  ******************************************************************************
  * @file    usb_com.c
  * @author  
  * @version 
  * @date    
  * @brief   
  ******************************************************************************
  *
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include	"main.h"
#include	"cmsis_os.h"
#include	"usbd_cdc_if.h"

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

#define 	RXBUF_MAX			256
#define 	TXBUF_MAX			64

/* Private macro -------------------------------------------------------------*/

#define 	_DLY		vTaskDelay					// 遅延
#define 	_MAL		pvPortMalloc				// RAM割当関数
#define 	_FRE		vPortFree					// RAM開放関数
#define 	_MTX		xSemaphoreCreateMutex		// ミューテックス生成
#define 	_TAKE		xSemaphoreTake				// 制御権獲得
#define 	_GIVE		xSemaphoreGive				// 制御権解放

/* Private variables ---------------------------------------------------------*/

static uint8_t	rxbuf[RXBUF_MAX];
static uint32_t rxlp			= 0;
static uint32_t rxsp			= 0;
static uint32_t rxcnt			= 0;

static uint8_t	txbuf[TXBUF_MAX];
static uint16_t	txcnt			= 0;
static SemaphoreHandle_t		txMtx = NULL;

/* Private function prototypes -----------------------------------------------*/

/* Private functions ---------------------------------------------------------*/

extern USBD_HandleTypeDef hUsbDeviceFS;

extern int usb_output(void);

/*
 *	USB送受信処理の初期化
 * 
 *	@param		なし
 *	@return		なし
 */
void USBCOM_init(void)
{

	// USB DPレベル→HI
	USB_DP_H;

	// 送信ミューテックス作成
	txMtx = _MTX();
	if (txMtx == NULL) {
		// 失敗
		while (1) { // 無限ループ
			LED_RED_TOGGLE;
			_DLY(100/MSEC);
		}
	}

	vTaskDelay(1000/MSEC); // 少しwaitしないとUSB関連処理でフリーズする

	return;
}

/*
 *	USBから送信するデータを登録します。
 * 
 *	@param		data 送信データ
 *	@param		size 送信データサイズ
 *	@return		(int) 送信バッファに登録されたデータサイズ
 */
int USBCOM_write(uint8_t *data, int size)
{
	uint8_t rsl;
	int txn = 0; // 送信数

	if (txMtx == NULL) return -1;
	_TAKE(txMtx, portMAX_DELAY);	// 制御権取得（ブロッキング(永久待ち)呼び出し！）

	while (size) {
		if (txcnt == TXBUF_MAX) {
			// 送信バッファフル状態
			stf_USB_TxFull = 1;
			do {
				_GIVE(txMtx);					// 制御権解放
				vTaskDelay(10/MSEC);			// 少し待つ
				_TAKE(txMtx, portMAX_DELAY);	// 制御権取得（ブロッキング(永久待ち)呼び出し！）
			} while (0 == usb_output());
			rsl = CDC_Transmit_FS(txbuf, txcnt);
			txn += (int)txcnt;
			txcnt = 0;
			if (rsl != USBD_OK) {
				// 再送失敗
				stf_USB_ReSentErr = 1;
				txn = -1;
				break;
			}
			txn += (int)txcnt;
			txcnt = 0;
		}
		// データコピー
		txbuf[txcnt++] = *data++;
		size --;
	}

	if (txcnt && usb_output()) {
		// 送信
		rsl = CDC_Transmit_FS(txbuf, txcnt);
		if (rsl == USBD_OK) {
			// 送信完了
			txn += (int)txcnt;
			txcnt = 0;	// インデックスクリア
		}
	}

	_GIVE(txMtx);		// 制御権解放

	return txn;
}

/*
 *	USB再送処理
 * 
 *	@param		なし
 *	@return		(int) 送信バッファに登録されたデータサイズ
 */
int USBCOM_rewrite(void)
{
	if (txcnt) {
		return USBCOM_write(NULL, 0); // 保留データ再送
	}
	return 0;
}

/**
 *	USBから受信したデータをもらいます。
 * 
 *	@param c 受信したデータを格納するバッファ
 *	@param size 受信したデータを格納するバッファのサイズ
 *	@return (int) 実際にバッファから読み込んだサイズ
 */
int USBCOM_read(uint8_t *data, int size)
{
	int num = 0;
	while (rxcnt && num < size) {
		*data++ = (char)rxbuf[rxlp++];		// get data from buffer
	    HAL_NVIC_DisableIRQ(USB_LP_IRQn);	// dis_int();
		rxcnt--;
	    HAL_NVIC_EnableIRQ(USB_LP_IRQn);	// ena_int();
		if (rxlp == RXBUF_MAX) rxlp = 0;	// buffer end?
		num++;
	}
	return num;
}

/**
 *	USBの受信バッファにたまっているデータ量を返します。
 * 
 *	@return		(int) 受信バッファにたまっているデータ量
 */
int USBCOM_rx_size(void)
{
	return (int)rxcnt;
}

/**
 *	USBが接続されているか検査
 * 
 *	@return		(int) 1=接続 0=切断
 */
int USBCOM_connect(void)
{
	return (hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED)? 1: 0;
}

/**
 *	端末が接続されているか検査
 * 
 *	@return		(int) 1=接続 0=切断
 */
/*
	★ 注意点
	ただし、この方法では起動直後やUSB挿抜状態によって正しく判定できないようだ。
	一度COM接続された後は挿抜で"USBD_EP0_IDLE←→USBD_EP0_STATUS_IN"と状態変化するが、
	起動直後はコンソールなしでも"USBD_EP0_IDLE"(接続？)と認識しているようだ。
*/
int USBCOM_terminal(void)
{
	return (hUsbDeviceFS.ep0_state == USBD_EP0_IDLE)? 1: 0;
}

/**
 *	USB受信データコールバック (割り込みハンドラ)
 * 
 *	@param
 *	@param
 *	@return
 */
void i_USBCOM_callback(uint8_t *data, int size)
{
	int num = size;
	while (num && rxcnt < RXBUF_MAX) {
		rxbuf[rxsp++] = *data++;			// stack data for buffer
		if (rxsp == RXBUF_MAX) rxsp = 0;	// buffer end?
		rxcnt++;
		num--;
	}
	return;
}


/*
 *	USBコンソールへ出力可能なタイミングか返す。
 *	output:
 *			0		NG
 *			1		OK
 */
int usb_output(void)
{
	/* プラットフォーム毎の条件による */
	return 1;
}
```

以上