圏9研究所 工作室

圏9研究所の開発情報資料など

STM32F411 BlackPill USB Speaker(8)同期

1.データ数同期
1)サンプリング数とDMA PWM出力数の同期
・TIM1割込関数にTransferComplete_CallBack_FS() とHalfTransfer_CallBack_FS()を追記する 

2)データ数調整
(1)データ数のコード
 ・コードを見ると AUDIO_AudioCmd_FS() の size数が±4 調整されている
 ・実測すると 7680 – 4 = 7676 となる場合がある
 ・標準のデータ数:AUDIO_OUT_PACKET_NUM 回分
  size基準値 = AUDIO_TOTAL_BUF_SIZE / 2 = サンプリング数 * AUDIO_OUT_PACKET_NUM * 2 = 7680
 ・実サンプリング数 = size  / 2

(2)同期手順
 ・サンプリング数を同期させているTIM1 Reload値を調整する
 ・ size が更新されるAUDIO_CMD_PLAY毎に再設定

// Update TIM1 to synchronize the number of audio data
	__HAL_TIM_SET_AUTORELOAD(&htim1, g_size / 2 -1);

STM32F411 BlackPill USB Speaker(7)メインシーケンス

1.メインシーケンス

seq

処理内容

次への遷移条件

SEQ_START

AUDIO_CMD_START

処理後

 

 初回audio data開始

 SEQ_PLAY

 

 ・DMAバッファポインタ初期化

 

 

 ・タイマー起動

 

 

 ・DMA起動

 

 

 

 

SEQ_PLAY

AUDIO_CMD_PLAY

haudio->alt_setting == 0U 再生停止

 

 audio data出力

 SEQ_PAUSE

 

 ・入力データ変換

 

 

  リトルエンディアン L R

 

 

  PWM 2000分割 duty50%0

 

 

 

 

SEQ_PAUSE

haudio->alt_setting != 0U 再生再開

処理後

 

 再生再開

 SEQ_START

 

 ・タイマー停止

 

 

 ・タイマー、DMAリセット

 

 

 ・USBD Out Data リスタート

 

 

 

 

 

2.各処理
1)AUDIO_AudioCmd_FS()    <usbd_audio_if.c>
・cmd値とデータバッファ情報を持ち出す

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
extern uint8_t *g_audio_buf;
extern uint32_t g_size;
extern uint8_t g_cmd;
/* USER CODE END PV */


static int8_t AUDIO_AudioCmd_FS(uint8_t *pbuf, uint32_t size, uint8_t cmd)
{
	/* USER CODE BEGIN 2 */
	switch (cmd)
	{
	case AUDIO_CMD_START:
		g_cmd |= AUDIO_CMD_START;
		break;
	case AUDIO_CMD_PLAY:
		g_cmd |= AUDIO_CMD_PLAY;
		g_audio_buf = pbuf;
		g_size = size;
		break;
	}
	return (USBD_OK);
	/* USER CODE END 2 */
}

STM32F411 BlackPill USB Speaker(6)USB audio data 転送処理

メインシーケンス作成のためデータハンドリング内容と定数を整理

1.オーディオデータ遷移
1)オーディオデータバッファとDAC PWMデータ遷移

 

関数

バッファ

クロック

処理

ホスト

ホスト

1msec毎に1 PACKETオーディオデータを送信

バイス

USBD_AUDIO_DataOut()

&haudio->buffer

データを順次バッファに保存

AUDIO_AudioCmd_FS()

pbuf

TransferComplete_CallBack_FS() によりバッファデータを伴いAUDIO_CMD_PLAY を発行

main()

pwm_buf

バイス

データをデコードして保存するAUDIO_OUT_PACKET_NUM 相当の時間が経過したらTransferComplete_CallBack_FS() を呼ぶ


2)再生スタートタイミング詳細(実測値)
・再生スタート時 AUDIO_CMD_START と AUDIO_CMD_PLAY 間は実測20.9usec
・AUDIO_CMD_START と AUDIO_CMD_PLAY 間で使える処理時間は 6.84usec(0.67 + 6.17)
・cmd値をUSB割込処理外のmain関数等に持ち出して処理するとUSB割込後の処理となる
 これは割込フラグが初期セットされているため発生する

 

2.オーディオデータ関連定数
1)定数一覧

シンボル

設定値

単位

内容

USBD_AUDIO_FREQ

48000

Hz

サンプリング周波数

AUDIO_OUT_PACKET

192

byte/PACKET

1パケットのバイト数

((USBD_AUDIO_FREQ * 2U * 2U) / 1000U)

AUDIO_OUT_PACKET_NUM

80U

PACKET/buff

バッファ収容パケット数

AUDIO_TOTAL_BUF_SIZE

15360

byte/buff

AUDIO_OUT_PACKET * AUDIO_OUT_PACKET_NUM

size

half word/buff

バッファ収容データ(16bit)

 AUDIO_AudioCmd_FS() 引数

2)タイマー類設定値一覧

所属

項目

設定値

設定コード・計算式

SYSTEM

システムクロック

SYSTEM_FREQ

96MHz

TIM2

Period

(TIM2_P_BASE)

2000-1

SYSTEM_FREQ/USBD_AUDIO_FREQ – 1

= TIM2_P_BASE - 1

TIM1

Period

(TIM1_P_BASE)

3840-1

AUDIO_TOTAL_BUF_SIZE/4 – 1

= TIM1_P_BASE - 1

Compare Pulse

1920-1

AUDIO_TOTAL_BUF_SIZE/4/2 – 1

= TIM1_P_BASE/2- 1

DMA PWM

バッファサイズ

AUDIO_DMA_BUF_SIZE

7680

AUDIO_TOTAL_BUF_SIZE/4 * 2

= TIM1_P_BASE * 2

保存開始

1920

AUDIO_TOTAL_BUF_SIZE/4 * 2 / 4

= AUDIO_DMA_BUF_SIZE / 4

 

(3)定数実装コード  <main.h> 

/* USER CODE BEGIN Private defines */
#define SYSTEM_FREQ 96000000U
#ifndef USBD_AUDIO_FREQ
/* AUDIO Class Config */
#define USBD_AUDIO_FREQ 48000U
#endif /* USBD_AUDIO_FREQ */
#define TIM2_P_BASE ((uint32_t)(SYSTEM_FREQ / USBD_AUDIO_FREQ))
#define TIM1_P_BASE ((uint16_t)(AUDIO_TOTAL_BUF_SIZE / 4))
#define AUDIO_DMA_BUF_SIZE ((uint16_t)(AUDIO_TOTAL_BUF_SIZE / 4 * 2))

/* USER CODE END Private defines */

STM32F411 BlackPill USB Speaker(5)USB接続ダウン対策

ホストとの接続が不安定なため対策しておきます

1. USB接続ダウン発生状況と対策
1)USB接続ダウン状況
(1)発生状況
 発生するのは
 ・USBコネクタ接続直後 USBリセット解除から80msec後付近
 ・再生停止
 ・ホスト側でのスピーカー選択解除
 概ね50%の確率でダウンするがリトライすれば連続3回程度を限度にして正常に接続される

(2)発生後の状態
 ホスト側  変化なし 出力デバイスとしての認識は解除さずデータも送信する
 デバイス側 機能停止 以後オーディオデータをバッファに送らない

2)ダウン検出処理
(1)割込フラグと処理
 USB処理はすべてUSB割込内で処理されているので割込処理を解析
 処理関数: HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd) <stm32f4xx_hal_pcd.c>

割込回

割込フラグ

処理

1

USB_OTG_GINTSTS_PXFR_INCOMPISOOUT

= OTG_FS_GINTSTS Bit21 INCOMPISOOUT:

    Incomplete isochronous OUT transfer

INCOMPISOOUT割込で

is_iso_incomplete = 1U

  hpcd->OUT_ep[epnum].is_iso_incomplete = 1U;  //epnum=1

2

USB_OTG_GINTSTS_OEPINT

= OTG_FS_GINTSTS Bit19 OEPINT:

    OUTendpointinterrupt

OEPINT割込で

is_iso_incomplete == 1U の場合

USBD_AUDIO_IsoOutIncomplete() <usbd_audio.c>が呼ばれる

 

3)ダウン対策
  USBD_AUDIO_IsoOutIncomplete()  に何も書かれていないのでお手本を参考にコードを追記
(1)お手本のコード USBD_AUDIO_IsoOutIncomplete()    <usbd_audio.c>

/**
  * @brief  USBD_AUDIO_IsoOutIncomplete
  *         handle data ISO OUT Incomplete event
  * @param  pdev: device instance
  * @param  epnum: endpoint index
  * @retval status
  */
// Fix suggested by Andrew to restart audio (fix Windows problem ?)
static uint8_t USBD_AUDIO_IsoOutIncomplete(USBD_HandleTypeDef* pdev, uint8_t epnum) {
	UNUSED(epnum);
	USBD_AUDIO_HandleTypeDef *haudio;
	haudio = (USBD_AUDIO_HandleTypeDef*)pdev->pClassData;

	USBD_LL_FlushEP(pdev, AUDIO_OUT_EP);

	/* Prepare Out endpoint to receive next audio packet */
	(void)USBD_LL_PrepareReceive(pdev, AUDIO_OUT_EP, (uint8_t *)&haudio->buffer[haudio->wr_ptr], AUDIO_OUT_PACKET_24B);

	return (uint8_t)USBD_OK;
}

 

(2)実装コード  USBD_AUDIO_IsoOutIncomplete()    <usbd_audio.c> 

static uint8_t USBD_AUDIO_IsoOutIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
  //UNUSED(pdev);
  UNUSED(epnum);

  // Fix suggested by Andrew to restart audio (fix Windows problem ?)
	USBD_AUDIO_HandleTypeDef *haudio;
  	haudio = (USBD_AUDIO_HandleTypeDef*)pdev->pClassData;

  	USBD_LL_FlushEP(pdev, AUDIO_OUT_EP);

  	/* Prepare Out endpoint to receive next audio packet */
  	(void)USBD_LL_PrepareReceive(pdev, AUDIO_OUT_EP, &haudio->buffer[haudio->wr_ptr], AUDIO_OUT_PACKET);

  	return (uint8_t)USBD_OK;
}

 

2.コンパイル最適化
 最適化すると動かない
 C/C++ Build : Setting : MCU GCC Compiler : Optimization : Optimization level    None(-O0) に設定

3.おまけ  
 おまけとして適切な対策が見つかるまでに使っていたリトライコードを参考に記載
(1)おまけリトライ
 USBソフトコネクト + CPUリセット
  USB接続ダウンを検出したら
  ・USB D+ ポートに L 出力
  ・10ms 以上WAIT
  ・CPUシステムリセット

(2)コード

#define RESTART_USB 	do{GPIO_InitTypeDef GPIO_InitStruct = {GPIO_PIN_12, GPIO_MODE_OUTPUT_PP};\
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);\
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);}while(0)

	RESTART_USB;
	HAL_Delay(12);
	HAL_NVIC_SystemReset();

 

おまけコードのお手本

community.st.com

 

4.解説
1)発生原因について
・原因不明
・処理している割込フラグからアイソクロナス通信に関するエラーのようですがアイソクロナスはDisableになっている
 アイソクロナス転送でのEVEN/ODD問題と同じように思われる
・USBパケットを追うと接続ダウン発生までの内容は発生有無にかかわらす同じように見える
・デバイス側のハードとホスト側の処理(OSや個体差)も絡むので原因究明は程々にしてお手本のコードを有難く使わせて頂いて対応

2)おまけのコード
・この件を対策しておかないと先へ進めなかったのでとりあえずの対策
 USBソフトコネクトは他でも有効に使える
・おまけお手本の待ち時間は500msecになっていますがUSBの規格は10msec以上なので12msecに設定
 WAIT設定値が適切かどうか不明であるが動く

次はオーディオデータの転送処理について

 

STM32F411 BlackPill USB Speaker(4)STM32CubeMX設定

1.設定一覧

2.STM32CubeMX タイマー設定

 

 赤枠内:TIM2カウンタアップデートをTIM1クロックとして使うために必要な設定

3.ポートマップ
(2)回路 に記載

4.解説
・TIM2_TRGO(TIM2出力)をTIM1クロック源にする設定
 TIM1のTrigger Source 設定でITR0を選んでクロック源のカウンタを選択する
 ITRxはリファレンスマニュアルに記載されているカウンタの組み合わせを見て設定する

・PWMのPulse設定値はPWM出力時にDMAで設定されるのでMXの設定値は任意
・PWM出力ポートはデフォルトでBlackPillボードに実装されているKEY(スイッチ) PA0に設定される
 PWM設定前にPA0を入力ポートに設定しておくとPWM出力はPA5に設定される

次はUSB接続について

 



 



 

STM32F411 BlackPill USB Speaker(3)PWM DAC構成

1.ブロック図

2.構成説明
1)PWM
 ・周期                        サンプリング周波数より 1/48kHz
 ・分解能                    システムクロック96MHzをクロックとして 2000

2)オーディオデータ
 ・USBデータバッファ合計    Middleware実装値 48 * 80 パケット: 15360byte/ms
 ・仕様と変換                         48kHz 16bit 2chトルエンディアン8bit区切りデータをPWM値に変換
 ・転送                                   サンプル数位相ズレ吸収のため数量2倍のバッファに格納しDMAでTIM2に転送

2)同期
 ・サンプル数同期               TIM2 カウンタアップデートをTIM1クロックとしてTIM1カウント値で同期
 ・サンプリング周波数        ホスト側周波数基準のUSB割込タイミングとTIM2 アップデートタイミングを比較
                                               TIM2 Period設定値にフィードバック

3.解説
・PWM用タイマーは32bitタイマー TIM2を使用しているが16bitタイマーを使えばバッファのメモリーサイズは半分に出来る
・ステレオ再生にするにはTIM2 PWMとバッファ、DMAを1組追加する

次はSTM32CubeMX設定



 

STM32F411 BlackPill USB Speaker(2)回路

1.ポートマップ

PIN

IO

EX

PU

PD

NAME

Function

OSC-IN

25MHz system clock

OSC-OUT

 

JTMS/SWDIO[PA13]

Debug SWD

JTCK/SWCLK[PA14]

 

PA11/USBDM

USB D-

PA12/USBDP

USB D+

PC13

O

LED

on board LED(Blue)

PA0

I

SW0

on board SW

PA5

O

PWM L

Lch output

PA1

O

PWM R

Rch output(未使用)

PB6

O

LED2

LED for debug

2.PWM DAC出力
 PWM出力AC化のためPWM出力からC107とR101直列でイヤフォンジャックに接続

3.レイアウト
・ブレッドボード            秋月:[P-00315] ブレッドボード EIC-801又は相当品
・イヤフォンジャック     秋月:[K-05363] 3.5mmステレオミニジャックDIP化キット

4.USB接続
 TypeAオス⇔TypeCオスケーブルでUSBハブに接続

5.ブレッドボード

6.解説
・PWM DAC出力回路は動作確認用のためDCカットのコンデンサとボリューム調整の抵抗のみです
・高調波ノイズが気になる場合は平滑化した電源で駆動するバッファが必要と思われます
・ブレッドボードは1個では配線がやりにくいので電源部片側を外した2個を繋げます
・このブレッドボードは何故か電源ラインが半ピッチずれているので無理矢理差し込んで使っています
・ミニジャック細ピンヘッダ1本のところはハンダ付けが難しいので何かでしっかり固定して作業します
・回路図は書くまでも無いので省略

次はUSBからDAC出力への構成