圏9研究所 工作室

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

STM32F411 BlackPill USB Speaker ISO(5) コード2/2

3)usbd_sp
(1)<usbd_sp.h>

/**
 Project Name: STM170_USB_SP_ISO

 File Name: usbd_sp.h

 Description:
 USB speaker with isochronous transfer
 Generation Information :
 Device			:  STM32F411CEU6
 Version			:  170
 Created on		: 2023/11/24
 */
/**
 Copyright (c) 2023 luke24e-hbid

 This software is released under the MIT License.
 http://opensource.org/licenses/mit-license.php
 */

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

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef INC_USBD_SP_H_
#define INC_USBD_SP_H_

/* Private defines -----------------------------------------------------------*/
#define SYSTEM_FREQ 96000000U
#ifndef USBD_AUDIO_FREQ
/* AUDIO Class Config */
#define USBD_AUDIO_FREQ 48000U
#endif /* USBD_AUDIO_FREQ */
#define DAC_BUF_SIZE ((USBD_AUDIO_FREQ * AUDIO_OUT_PACKET_NUM) / 1000U)
#define TIM2_P_BASE ((uint32_t)(SYSTEM_FREQ / USBD_AUDIO_FREQ))
// #define TIM2_P_BASE			2000
#define AUDIO_ISO_FS_INIT 48000U
#define AUDIO_ISO_FS_DMAX 750U
#define TIM1_P_BASE DAC_BUF_SIZE
#define DMA_BUF_SIZE2 ((uint16_t)(DAC_BUF_SIZE * 2))
#define DAC_BUF_SIZE4 ((int32_t)(DAC_BUF_SIZE * 4))

// Isochronous Async endpoint
/* Input endpoint is for feedback. See USB 1.1 Spec, 5.10.4.2 Feedback. */
#define AUDIO_IN_PACKET 3U
#define USBD_EP_TYPE_ISOC_ASYNC 0x05U
#define AUDIO_IN_EP 0x81U
#define SOF_RATE 0x02U // bRefresh 4ms = 2^2

// Main Sequence enum
typedef enum
{
	SEQ_IDLE,
	SEQ_START,
	SEQ_PLAY,
	SEQ_PAUSE,
} SEQ_MAIN;

// USBD AUDIO DataOut Sequence enum
typedef enum
{
	RD_IDLE,
	RD_START,
	RD_PLAY,
} SEQ_RDEN;

// USBD AUDIO DataOut wr_ptr status
typedef enum
{
	WR_PTR_IDLE,
	WR_PTR_1st,
	WR_PTR_INQ,
} STATE_WR_PTR;

// USBD AUDIO Data decode union
typedef union
{
	int16_t i16t;
	uint8_t u8t[2];
} U8T2I16T;

// ISO Control Value
typedef struct
{
	uint32_t fs;							 // ISO Feed Back Value
	volatile uint32_t tx_flag; // ISO busy flag
	volatile uint32_t fnsof;	 // OTG_FS_DSTS.FNSOF:Frame number of the received SOF
	uint32_t ofs_packet;			 // Offset of Packet
	uint16_t usbintn;					 // Number of USB INT.
	//	uint16_t			ofs_dac;		// Offset of DAC buffer
	//	int32_t				ofs_diff;		// Difference between Packet and DAC buffer
} ISO_ControlTypeDef;

/* Function prototype declaration ------------------------------------------*/
STATE_WR_PTR usbd_audio_wrinq(STATE_WR_PTR stin);
void usbd_sp_iso(void);

#endif /* INC_USBD_SP_H_ */

STM32F411 BlackPill USB Speaker ISO(5) コード1/2

1.コード
1)main
(1)<main.h>

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usbd_sp.h”

STM32F411 BlackPill USB Speaker ISO(4) ファイル、定数、変数、シーケンス

1.ファイル一覧
1)mainシーケンス関連

ファイル名

修正

追加

<main.h>

#include "usbd_sp.h"

<main.c>

usbd_sp() 呼び出し

<stm32f4xx_it.c>

 

デバッグ

<usbd_sp.h>

定数定義

<usbd_sp.c>

メインシーケンス実装

2)usbd audio関連

ファイル名

関数/定義

修正追加内容

<usbd_audio.h>

#define USB_AUDIO_CONFIG_DESC_SIZ

・デスクリプタサイズ変更

(エンドポイント追加用)

USBD_AUDIO_HandleTypeDef

・メンバー使用方法変更

<usbd_audio.c>

#define AUDIO_PACKET_SZE(frq)

・パケットサイズ拡張

Endpoint 1

AUDIO_OUT_EP修正

Endpoint 2

AUDIO_IN_EP追加

変数

USBD_AUDIO_HandleTypeDefに追加 詳細別途

・その他変数追加

USBD_AUDIO_Init()

・エンドポイント追加

・フィードバック返信 busy : tx_flag=1

USBD_AUDIO_DeInit()

・フィードバック返信 idle : tx_flag=0

USBD_AUDIO_SOF()

・フィードバック返信処理

USBD_AUDIO_IsoINIncomplete()

・フィードバック返信復旧 dle : tx_flag=0

USBD_AUDIO_IsoOutIncomplete()

DataOut復旧

USBD_AUDIO_DataOut()

・データハンドリング変更

・フィードバック値算出追加

<usbd_conf.c>

USBD_LL_Init()

SOF enable

 

2.オーディオデータ関連定数

1)USBD AUDIO

シンボル

設定値

単位

内容

USBD_AUDIO_FREQ

48000

Hz

サンプリング周波数

AUDIO_OUT_PACKET

192+4

byte/PACKET

1パケットのバイト数  48±1 * 4

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

AUDIO_OUT_PACKET_NUM

80U

PACKET/buff

バッファ収容パケット数

AUDIO_TOTAL_BUF_SIZE

15360+320

byte/buff

AUDIO_OUT_PACKET * AUDIO_OUT_PACKET_NUM

USBD_EP_TYPE_ISOC_ASYNC

0x5U

 

bmAttributes

AUDIO_IN_EP

0x81U

 

bEndpointAddress : ISO Input Endpoint

AUDIO_IN_PACKET

3U

byte

Input Endpoint パケットサイズ

SOF_RATE

0x2U

 

P値 bRefresh : 4ms = 2^2

AUDIO_ISO_FS_INIT

48000

 

fs基準値

AUDIO_ISO_FS_DMAX

750

 

fsフィードバック偏差最大値

2)DAC

所属

項目

設定値

設定コード・計算式

SYSTEM

システムクロック

SYSTEM_FREQ

96MHz

USBD_SP_ISO

DACバッファサイズ[word]

DAC_BUF_SIZE

3840

((USBD_AUDIO_FREQ * AUDIO_OUT_PACKET_NUM)/1000U)

DACバッファサイズ[byte]

DAC_BUF_SIZE4

15360

DAC_BUF_SIZE * 4

TIM2

Period

(TIM2_P_BASE)

2000-1

SYSTEM_FREQ/USBD_AUDIO_FREQ – 1

= TIM2_P_BASE - 1

TIM1

Period

(TIM1_P_BASE)

3840-1

DAC_BUF_SIZE – 1

= TIM1_P_BASE - 1

DMA PWM

DMAダブルバッファサイズ

DMA_BUF_SIZE2

7680

DAC_BUF_SIZE * 2

保存開始

1920

DMA_BUF_SIZE2 / 4

 

3.変数一覧
1)USBD_AUDIO 変更追加分

所属

変数名

G/L

内容

<usbd_audio.h>

USBD_AUDIO_HandleTypeDef haudio

G

構造体 使い方を一部変更

  uint32_t     alt_setting;

 

0:再生停止

1:再生中

  uint8_t       buffer[];

 

AUDIO DATA バッファ

  AUDIO_OffsetTypeDef     offset;

 

AUDIO_OFFSET_NONE:データ受信中

AUDIO_OFFSET_HALF

AUDIO_OFFSET_FULL:データ受信完了

AUDIO_OFFSET_UNKNOWNSTART

  uint8_t       rd_enable;

 

RD_IDLE:再生開始前

RD_START:初回データ受信中

RD_PLAY:データ受信中

  uint16_t     rd_ptr;

 

DACに渡すデータ数[byte]

  uint16_t     wr_ptr;

 

AUDIO DATAバッファへデータ格納済の数

  USBD_AUDIO_ControlTypeDef     control;

 

制御関連(詳細未調査)

ISO_ControlTypeDef iso_cont

G

構造体 アイソクロナス返信用として新設

  uint32_t fs = AUDIO_ISO_FS_INIT;

 

fs

  volatile uint32_t    tx_flag = 1U;

 

0:ISO返信 idle

1:ISO返信 busy

  volatile uint32_t    fnsof = 0;

 

SOFフレーム番号

 ISO返信同期に使用

USBD_AUDIO_DataOut()

  uint32_t    ofs_packet;

(L)

USBデータバッファオフセット

  uint16_t    usbintn;

(L)

受信したパケット数

uint16_t    ofs_dac;

L

DACバッファオフセット

int32_t      ofs_diff;

L

バッファオフセット偏差

2)USBD_SP

所属

変数名

G/L

内容

<usbd_sp.c>

USBD_AUDIO_HandleTypeDef *haudio;

L

USBD AUDIO Handle <usbd_audio.c>

SEQ_MAIN  seq_m = SEQ_IDLE;

L

メインシーケンス state

uint32_t  cnt_pwm_p;

L

PWM DAC バッファポインタ

uint32_t  pwm_buf[DMA_BUF_SIZE2];

L

PWM DAC バッファ

U8T2I16T  dec;

L

オーディオデータデコード共用体

int32_t  dp32[2];

L

オーディオデータ [0]:L ch, [1]:R ch

uint32_t  dpout;

L

PWM DAC バッファデータ 1000±1000

 

5.シーケンスとデータフロー

処理順

rd_enable

SEQ_RDEN

状態

処理

遷移

RD_IDLE

USDBデータ受信前

再生停止

USBD DataOut リスタート

 バッファポインタ,カウンタ初期化

 ISO起動

 RD_START

 WD_PTR_1st

RD_START

 無条件

 継続処理

RD_START

初回パケット

バッファ格納完了

USBDデータ受信

FS算出

パケットバッファ格納完了時

 RD_START:RD_PLAY 継続処理

 RD_PLAY:AUDIO_OFFSET_FULL

RD_PLAY

 パケットバッファ格納完了

 継続処理

⑦⑨

RD_PLAY

再生

RD_IDLE

 SEQ_PAUSEから再生再開

 

処理順

seq_m

SEQ_MAIN

状態

処理

遷移

 

SEQ_IDLE

USDBデータ受信前

USBDデータ受信待ち

 WR_PTR_IDLE

 USBD_AUDIO_HandleTypeDef取得

 SEQ_START

SEQ_START

 WR_PTR_1st

SEQ_START

DAC起動

USBDデータ受信

 タイマー, DAC起動

 SEQ_PLAY

SEQ_PLAY

 AUDIO_OFFSET_FULL

 データはSEQ_PLAYで処理

SEQ_PLAY

再生

USBDデータ受信

 AUDIO_OFFSET_NONE

 データデコードしバッファへ格納

再生停止

 SEQ_PAUSE

SEQ_PAUSE

 alt_setting == 0U

⑩以降

から遷移

SEQ_PAUSE

再生停止

再生再開

 タイマー, DACリセット

 RD_IDLE

 SEQ_IDLE

SEQ_IDLE

 alt_setting != 0U

 

6.解説
・USBD_AUDIO_HandleTypeDef haudio は一部メンバーを転用し使い方を変更
・rd_enable再生開始で RD_IDLE→RD_START→RD_PLAY は USBD_AUDIO_DataOut() 内で継続処理

 

次はコード

STM32F411 BlackPill USB Speaker ISO(3) STM32CubeMX設定

1.設定一覧

2.ポートマップ

 

3.解説
 前回比
 ・TIM1はデバッグ専用
 ・ポートにSWO追加
  次のプロジェクトのデバッグでprintfを使うため コード未実装

 

次はファイル構成等

STM32F411 BlackPill USB Speaker ISO(2)アイソクロナス転送

アイソクロナス転送の詳細

1.エンドポイント
1)仕様書
 Universal Serial Bus Device Class Definition for Audio Devices Release 1.0 March 18, 1998
 4.6 AudioStreaming Endpoint Descriptors

2)受信用エンドポイント Audio Data Endpoint Descriptor
 アイソクロナス転送タイプに変更 bmAttribures = 0x05

3)返信用エンドポイント Isochronous Synch Endpoint Descriptor
 追加
 ・フィードバック周期を bRefreshで設定する

2.フィードバック仕様
1)仕様書
 Universal Serial Bus Specification Revision 1.1 September 23, 1998
 5.10.4.2 Feedback

2)フィードバックデータ
(1)フィードバックデータ Ff
 An asynchronous sink provides feedback to an adaptive source by indicating accurately what its desired data rate (Ff) is, relative to the USB SOF frequency.

 USB SOF 周波数(ホスト側1kHz)に対して必要なデータ レート (Ff サンプル数) 

(2)Ff値データフォーマット
 Because the maximum integer value is fixed to 1,023, the 10.10 number will be left- justified in the 24 bits, so that it has a 10.14 format.

 固定2進小数点 24ビット(3バイト)10.14 形式

3)Ff値返信
 Isochronous transfers are used to read Ff from the feedback register. The desired reporting rate for the feedback should be 2^(10-P) frames. Ff will be reported at most once per update period. 

 P値による周期で更新
 エンドポイント bRefresh = (10-P)

4)ホスト側の処理
 It is possible that the source will deliver one too many or one too few samples over a long period, due to errors or accumulated inaccuracies in measuring Ff. 

 サンプル数を±1して配信

 

3.フィードバック値の例
 わかりやすい例としてUSB SOF周波数を基準とした計算例

1)フィードバックデータ Ff
(1)計算例設定値
 サンプリング周波数            48,000[Hz]
  USB SOF                 1,000[Hz]
 デバイス側周波数            48,048[Hz]
  ホスト側を基準として計測
  デバイス側が速い場合

(2)Ff 計算式
 デバイス側が速いのでサンプル数48より多く必要
  Ff = 48048/48000 * 48 = 48.048
  fs = Ff * 1000
  Ff 整数部 = fs / 1000
  Ff 小数部 = fs % 1000

(3)10.14形式変換 24ビット(3バイト)データ rate
 rate=(((fs / 1000) << 14)|((fs % 1000) << 4));

 

4.実装するフィードバック値計算
 計算例ではわかりやすいように周波数比で計算した
 実際は受信データ数とDAC送出データ数が同期しないといけない
 受信データ数とDACデータバッファポインタ位置の位相差でフィードバック値を計算する

1)同期イメージ

2)フィードバック値算出 USBD_AUDIO_dataOut()毎処理
・ホストから送信されたデータ数を基準とするためデータパケット受信処理に合わせて算出する
・更新周期はP値で設定した周期毎とする
・フィードバックが発散しないように制限を設けておく

タイミング

名称[単位]

内容

処理

毎回

ofs_packet[byte]

USBD Packet data offset

USBD側オフセット

USBD Packet data 数を累計

15360で正規化

4回毎

P:4msec

ofs_dac[word]

DAC側オフセット

DMA転送残数を取得し算出

ofs_diff[byte]

バッファ位相差

 バッファ中央値を基準に

 ±振り分ける

ofs_diff = ofs_packet - ofs_dac * 4

if (ofs_diff < -15360/2)    ofs_diff = ofs_diff + 15360;

if (ofs_diff > 15360/2)     ofs_diff = ofs_diff - 15360;

fs

フィードバック値

fs - ofs_diff * 48000 / 15360

fs基準値 48000

fs値制限 48000±750

 

5.フィードバック値返信タイミング
・お手本にならってタイミングを合わせる
・tx_flag により返信状況を管理する

1)返信処理一覧

関数

tx_flag

処理

USBD_AUDIO_DataIn()

0

tx_flag = 0;

USBD_AUDIO_IsoINIncomplete()

0

FNSOF値入手

tx_flag = 0;

エンドポイントフラッシュ

USBD_AUDIO_SOF()

1

FNSOF値入手

 if IsoINIncomplete()FNSOFbit 0同じ:

  フィードバック値返信

  tx_flag = 1;

2)FNSOF EVEN/ODD対策
・STM32F411内臓のUSBインターフェースでは受信SOFフレーム番号の偶数奇数位相を合わせないと返信できない
  FNSOF:Frame number of the received SOF
  OTG_FS_DSTS    Bits21:8
・返信できなかった場合 USBD_AUDIO_IsoINIncomplete()が呼ばれるので位相合わせ処理を行う

参考資料

community.st.com

 

6.フィードバック収束状況
 ・1sec程度で収束する
 ・収束値は計算基準値の約2倍
  計算基準値:48000 * ±0.5%= ±240
  実測値: 約±500

 

7.データハンドリング処理
 USBで受信したデータをデコードしバッファを介してDACに転送する
 ・80パケット毎にUSB受信バッファに蓄えたデータを処理する
 ・デコードしてDACバッファに格納する
 ・DMAでPWM DACへ転送(DMA_CIRCULAR)
 ・STM32CubeIDE MXのMiddlewareが生成する同期関数
  USBD_AUDIO_Sync(), TransferComplete_CallBack_FS(), HalfTransfer_CallBack_FS() は使用しない
 ・再生、停止処理は前回と同じ

 

8.解説
・P値はお手本の値を基にフィードバック収束の状況を見て決定
・フィードバック制限値はフィードバック収束値と偏差によって決める
 フィードバック制御可能な偏差許容値は ±1/48=±2.08% at 48kHz
・フィードバック収束状況はホストにより異なると思われる
・USB SOF周波数はTIM2_ITR1に接続して計測できる
 RM0383 Reference manual STM32F411xC/E advanced Arm®-based 32-bit MCUs

次はSTM32CubeMX設定

 

STM32F411 BlackPill USB Speaker ISO(1)概要

1.経緯
 宿題のアイソクロナス転送 USB Speaker

2.システム概要
STM32F411 BlackPill USB Speaker をアイソクロナス転送にする
・ハードウェアは変更なしソフトのみ変更

3.手順
1)アイソクロナス転送実装
・受信用エンドポイントをアイソクロナス転送タイプに変更
・返信用エンドポイント追加
・仕様書とお手本からホストへ返信する同期データとタイミングを調査し実装

2)データハンドリング
 アイソクロナス転送用に変更

4.お手本
 STM32F411 BlackPill USB Speaker(1)概要と同じ

5.解説
・ハードウェアは前回と同じSTM32F4が実装されたBlackPillを使用しDACも同じPWM
・STM32CubeIDE MXのMiddlewareが生成する同期コードは使用しない
・STM32CubeIDE MXの生成するコードの修正は極力<usbd_audio.c>で対応できるようにする

次はアイソクロナス転送詳細

 

STM32F411 BlackPill USB Speaker(9)まとめ

1.追加変更コード一覧
 main.h と main.c に書いたメインシーケンス処理コードを別ファイルにしてまとめる

dir

File

Function

内容

Core

usbd_sp.h

追加定数等定義

usbd_sp.c

usbd_sp_iso()

メインシーケンス

main.h

定数定義 include

main.c

メインシーケンス呼び出し

stm32f4xx_it.c

TIM1_UP_TIM10_IRQHandler()

サンプリングクロック同期

TransferComplete_CallBack_FS();

TIM1_CC_IRQHandler()

HalfTransfer_CallBack_FS();

OTG_FS_IRQHandler

サンプリングクロック同期

Middlewares

usbd_audio.c

USBD_AUDIO_IsoOutIncomplete()

USB接続ダウン復旧

USB_DEVICE

usbd_audio_if.c

AUDIO_AudioCmd_FS()

再生イベントとデータハンドリング

 

2.結果と課題
1)結果
・シンクロナス転送でのデータ受取実装完了
 STM32CubeIDE(MX)のUSB audio Middlewaresに僅かな追加で実装できる
 ただし追加コードに関する情報は少ない

2)課題
・アイソクロナス転送実装
・USBマイクコード実装

3.USER CODE ファイル
1)<usbd_audio>_if.c  AUDIO_AudioCmd_FS()
 (7)メインシーケンス に記載

2)<usbd_audio.c>  USBD_AUDIO_IsoOutIncomplete()
 (5)USB接続ダウン対策 に記載

3)<stm32f4xx_it.c>
 (8)同期 に記載

4)<main.c> 関数追記

 /* USER CODE BEGIN WHILE */
  usbd_sp_iso();