FreeRTOS上でSTM32CubeMXを利用したUSB仮想シリアルポート利用コードの記録です。
ターゲットマイコン:STM32L152RE
一般的サンプルをアレンジした参考ソースコードとなります。
元ソースコードの生成方法や、一部の関数、変数等については説明を割愛いたします。
特記事項:
内部送信バッファがオーバーフローすると送信が停止するため、
送信残データはタイマー処理等でUSBCOM_rewrite()を呼び出し吐き出す工夫が必要です。
【注意事項】
本記事中のソフトウェアはサンプルコードであり、ソフトウェアを使用した結果いかなる損害等が発生しても当方は一切責任を負いません。
●ソースコード
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;
}
以上