HAL_I2SEx_TransmitReceive_DMA使用時の割り込み修正

Owm Pedal ソフトウェア編で書いていた通り、「HAL_I2SEx_TransmitReceive_DMA」使用時に「HAL_I2SEx_TxRxCpltCallback」が起こらないというバグがあります。STM32のコミュニティで質問されていましたがその後のバージョンでも修正されていないので、自力で修正することにします。



プロジェクトフォルダ内のファイルDrivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2s_ex.cを編集します。「I2SEx_TxRxDMACplt」関数がDMA転送完了時に呼び出されるようになっています。この関数を見ると、「if (hdma->Init.Mode == DMA_NORMAL)」以下のカッコ内に「/* Call user TxRx complete callback */」以下の記述が入っているため、DMAサーキュラーモードでは何も起きないことがわかります。なのでこの記述をカッコ外に移動すれば解決するはずです。(同フォルダ内にあるstm32f4xx_hal_i2s.cの「I2S_DMATxCplt」関数を見ると正しいコードがわかります。)

細かい修正をすると間違えそうなので、私は単純にHAL_I2SEx_TxRxCpltCallback関数を追加記載することにしました。
static void I2SEx_TxRxDMACplt(DMA_HandleTypeDef *hdma)
{
I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;

HAL_I2SEx_TxRxCpltCallback(hi2s); // ←追加!CubeMXでコード出力すると元に戻るので注意

/* if DMA is configured in DMA_NORMAL mode */
if (hdma->Init.Mode == DMA_NORMAL)
{
if (hi2s->hdmarx == hdma)
{
/* 中略 */
}

if (hi2s->hdmatx == hdma)
{
/* 中略 */
}
}
}

<データ処理方法変更>
1つのブロックにつき「HAL_I2SEx_TxRxHalfCpltCallback」と「HAL_I2SEx_TxRxCpltCallback」の2回割り込みが使えるようになったので、Owm Pedalの信号処理を下図のように変更しました。GitHubのmain.cファイルも変更しています。
03s_239_1owmI2S.png
最初の割り込みでグループA前半の信号処理と送信バッファへの代入を開始し、次の割り込みでグループA後半を処理開始します。処理後のグループA(A')を送信するときには送信バッファへの代入が余裕をもって終わっていると考えられるので、安定した動作が見込めます。ただし、実質的にブロックサイズ半分で処理しているという点には注意が必要です。

タグ : マイコン 

Owm Pedal ソフトウェア編

自作デジタルエフェクター「Owm Pedal」の各設定、内部プログラミングをしていきます。
Owm Pedal ハードウェア編はこちら
内部データ、STM32CubeMX用iocファイルはGitHub



<ピン、クロック設定>
・ST-LINK接続
STM32CubeMX設定:System Core→SYS→Debug [Serial Wire]

上写真右上に見える2つのジャンパを外し、NucleoボードのST-LINK部分のみ使用します。SWDピンをそのままの順番でOwmボードへ接続できます。今回は外部から電源供給するため、3.3Vピンは接続しません。

・クロック源として外部水晶振動子を使用
STM32CubeMX設定:System Core→RCC→High Speed Clock (HSE) [Crystal/Ceramic Resonator]

▽クロック設定図

I2Sクロックは、オーディオサンプリングレート48kHzに対する誤差が少なくなるような設定としました(48×2048=98304)。

▽全ピン設定図

PC2はデバッグ時オシロスコープを接続する場合に利用しています。

<スイッチ>
割り込みではなくメインループで処理しています。今までAVRマイコンでやっていたカウントを増やす方式なので、長押しにも対応できます。

<ADC>
設定方法は過去記事(→NucleoボードのADCを使う)と同じで、チャンネル数が増えるだけです。ポットの値の取得は低速で構わないため、Sampling Time : 144 Cycles としています。

<I2S>
設定方法は過去記事(→NucleoボードとオーディオコーデックICとの通信)と同じですが、I2Cは不使用です。信号処理を優先するため、DMAのPriorityは[Very High]に、他の割り込みの優先度は下記の通り変更しました。
STM32CubeMX設定:System Core→NVIC→
 Time base: System tick timer→Preemption Priority [10]
 DMA2 stream0 global interrupt→Preemption Priority [5]

今回は受信したデータを信号処理後に送信することになります。I2Sの受信バッファに半分データがたまると「HAL_I2SEx_TxRxHalfCpltCallback」という関数(以下Half関数)が呼び出されます。しかしデータが全て溜まったときの「HAL_I2SEx_TxRxCpltCallback」は呼び出されないバグがあるようです。修正方法がわからない(あまり調べていません)ので、このままHalf関数のみで処理するようにしました。Half関数は送信と受信で2回起こってしまうので、送信側(DMA1 Stream4)の割り込みは無効化しています。

※処理方法を変更しました。→HAL_I2SEx_TransmitReceive_DMA使用時の割り込み修正(2019年3月25日追記)

下図はブロックサイズを32(ステレオなのでバッファ配列の要素数は64)で処理する場合のものです。配列の1グループは16...31, 0...15の順番となります。
03s_238_4owmI2S.png
最初の割り込みでグループAを一旦全て信号処理配列へ移し、次の割り込みで信号処理後のグループA(A')を送信バッファに送ります。このときA'[16]は送信されずA'[17]から送信されてしまいます。そこで処理後データA'を送信バッファへ代入するとき、1サンプルずらしています。もっとスマートに解決する方法(FIFO?)がありそうですが、現状問題が起こっていないので追求していません。遅延時間(レイテンシ)実測値は、ブロックサイズ16で1.7ms、32で2.4ms程度でした。

通常のやり方は、最初の割り込みの時にグループAのデータを1つずつ信号処理と送信を行うというものだと思います。しかしその場合たまに波形が乱れることがあったので、今回は少し回りくどい方法を採用しています。遅延が余計に発生してしまいますが…


<I2Sエラー対処>
たまにI2S通信がうまくいかないことがありましたが、フレームエラーというのが起こっていました。マスター(オーディオコーデックV4220M)がクロック送信する前にスレーブ側(マイコン)のI2S設定をする方がいいようです。デバッグ時は先にV4220Mが起動しているためかエラーが起こりやすく、エラー時はソフトリセットで対応するようにしました。本来はI2Sのみのリセットで済むかもしれませんが、DMAを使っているため全リセットするのが確実でしょう。電源を入れなおした際はV4220Mが後から起動するため、エラーはほぼ発生しません。とはいえオーディオコーデックのリセットピンはマイコンと繋げておくべきだったと思います。

<エフェクト処理>
main.cとmain.hのユーザーコード部分を編集する以外に、2つのファイル(fx.c、overdrive.c)を追加しています。詳細はコード内のコメントをご参照ください。エフェクトはとりあえずオーバードライブで、操作はフットスイッチ(バイパス)、左上ポット(LEVEL)、右上ポット(GAIN)、中央LEDのみ使っています。

overdrive.cでフィルタを使っていますが、過去のデータ(x1、y1)を利用するため、static修飾子を付けて前の計算結果を残したままにしておく必要があります。その結果、フィルタの数だけ関数を準備しておく状態となっています。今後複雑なエフェクトに対応するために、C++言語を使う必要性が出てきそうです。

タグ : 自作エフェクター マイコン 

Owm Pedal ハードウェア編

03s_237_1owmp.jpg
STM32F405という32ビットマイコンを搭載した自作デジタルエフェクター「Owm Pedal」です。名前は同じマイコンを搭載した既存のペダル「OWL Pedal」をもじってつけました。(Owm Pedal ソフトウェア編はこちら

オーディオコーデックはCS4220のセカンドソース品V4220Mです。サンプリングレートが48kHzまでですが差動入出力で価格が安く(秋月電子で240円)、エフェクターに最適だと考えました。

▽回路図
03s_237_2owms.png
<V4220M周辺>
データシートに入出力の回路が記載してあるのですが、抵抗値はよく使う値へと変更しました。バイアス電圧用に8.25kΩの抵抗があるところは、10kΩと100kΩ2個を並列にして8.33kΩとしています。

V4220Mのデータシートでは音量等をコントロールできそうに書いてありますが、実際はできないようです。マスターモードで動作させる場合は8番(DOUT)ピンに47kΩのプルダウン抵抗を入れます。また、CS4220のデータシートには電源オン時に27番(RSTN)ピンを10msの間LOWにしておくように書いてあるため、10uFのコンデンサを入れました。4・5・8・9番ピンからマイコンへ接続しますが、通信線の長さが短いためダンピング抵抗は不要かと思います。

<電源>
電源はレギュレータで以下のように分けました。
・マイコン用→デジタル3.3V(100mA)
・V4220M用→デジタル5V(20mA) デジタル3.3V(5mA) アナログ5V(60mA)
・OPA1678×3用→アナログ5V(20mA)
アナログ5V電源は通常分離する必要はありませんが、万一問題があったとき基板発注し直すのが嫌なので分けています。

▽DSP基板(Owm Board)レイアウトについて(KiCadデータはGitHubへ)
真ん中あたりに電源、上側がデジタル、下側がアナログという配置となっています。GNDは裏面を一面プレーンにしました。入力のカップリングコンデンサはPMLCAPを使っており、やや大きくて高価ですが歪率は下がるでしょう。残念ながらBIASに接続すべきところをGNDにつなぐというありがちな間違いをしてしまいましたので、内部写真では妙なジャンパー線が写っています(KiCadデータは修正済)。

ピン間隔が狭いICはパッドを1mm程度長くすると半田付けしやすいです。マイコンのピンはほとんど使えるように引き出しました。一応I2C用にプルアップ抵抗の取り付けもできます。マイコン上側のLEDはデバッグ用のつもりです。水晶振動子周りのパターン設計は下記ページを参考にしました。
水晶振動子 ガイド - RSオンライン

▽ポット類基板レイアウト
03s_237_3owml.png
ポット類基板と筐体はRasPd4のものを使いまわしました。回路図は描いていません。無理やりジャンパーを飛ばしてチャタリング対策の抵抗やコンデンサを入れました。下写真のように基板を合体させます。
03s_237_4owmpp.jpg

とりあえず何もエフェクトをかけないスルー音が出るようにプログラミングし、周波数特性と歪率を測定しました。歪率は、クリップしない最大入力約0.7Vrmsでの結果です。
03s_237_5owmf.png
100Hzの歪率が思ったより悪いですが問題ないでしょう。ノイズも測定限界以下だったので、歪み系エフェクトでもおそらく大丈夫だと思います。

タグ : 自作エフェクター レイアウト 回路図 マイコン 周波数特性 歪率 

STM32 NucleoボードとオーディオコーデックICとの通信

03s_234_1nuwmp.jpg
高品質なアナログデジタルコンバータ(ADC)とデジタルアナログコンバータ(DAC)が搭載されたオーディオコーデックと呼ばれるICがあります。デジタル信号処理をする場合はこのICを使いこなすことが必要です。ほとんどのオーディオコーデックはI2C(Inter-Integrated Circuit)とI2S(Inter-IC Sound)という通信方式を使うことが可能です。

今回はWM8731というICが搭載されたAUDIO CODEC Board(MIKROE-506)を使用します。I2C通信によりデータフォーマット等の設定を行い、I2S通信でオーディオデータを送受信します。今回もADCのときと同様DMAを使いました。



Nucleoボード(NUCLEO-F303RE)とMIKROE-506との接続は下図の通りです。

MIKROE-506はC23を1nF→2.2uFへ変更し、R34は取り外しています。特に必須な改造ではありませんが、C23を大きな値に変更しないと音量がかなり下がってしまうと思われます。MISO等の表記はMIKROE-506上の表記に合わせました。



<STM32CubeMX(5.0.0) Pinout & Configurationタブ>
左側列のConnectivity→I2C1を開く

・中央列上側 Mode
 I2C : I2C 

・中央列下側 Configuration→Parameter Settingsタブ
03s_234_3nuwmss1.png
 Rise Time (ns) : 1000
 Fall Time (ns) : 300
※STM32F303がマスター(送信側)、WM8731がスレーブ(受信側)

・右側列 IC画像
 ピン位置を変更
  (61)-PB8 : I2C1_SCL
  (62)-PB9 : I2C1_SDA

左側列のMultimedia→I2S2を開く

・中央列上側 Mode
Mode : Full-Duplex Slave ※STM32F303がスレーブ(受信側)、WM8731がマスター(送信側)

・中央列下側 Configuration→Parameter Settingsタブ
03s_234_4nuwmss2.png
Data and Frame Format : 16 Bits Data on 32 Bits Frame
Selected Audio Frequency : 48KHz

・中央列下側 Configuration→DMA Settingsタブ
03s_234_5nuwmss3.png
Addボタンで2行追加する
 DMA Request : SPI2_RX
  Mode : Circular
  Data Width (Peripheral) : Half Word
  Data Width (Memory) : Half Word
 DMA Request : SPI2_TX
  Mode : Circular
  Data Width (Peripheral) : Half Word
  Data Width (Memory) : Half Word



<TrueSTUDIO(9.1.0)>
main.cに2箇所追加記載する

/* USER CODE BEGIN 0 */の下 I2Cで送信するデータ、I2Sで受信するデータを定義
uint8_t DevAddr = 0b00110100;
uint8_t pdccAddr = 0b00001100 ; //Power Down Control
uint8_t pdccData[1] = {0b00000000};
uint8_t daifAddr = 0b00001110 ; //Digital Audio Interface Format
uint8_t daifData[1] = {0b01000010}; //48kHz 16bit I2S Master Mode
uint8_t aapcAddr = 0b00001000 ; //Analog Audio Path Control
uint8_t aapcData[1] = {0b00010100};
uint8_t dapcAddr = 0b00001010 ; //Digital Audio Path Control
uint8_t dapcData[1] = {0b00000000};
uint8_t aaccAddr = 0b00010010 ; //Active Control
uint8_t aaccData[1] = {0b00000001};
uint16_t RX_BUFFER[24] = {};

/* USER CODE BEGIN 2 */の下 データを送受信
HAL_I2C_Mem_Write(&hi2c1,DevAddr,pdccAddr,1,(uint8_t*)pdccData,1,1000);
HAL_I2C_Mem_Write(&hi2c1,DevAddr,daifAddr,1,(uint8_t*)daifData,1,1000);
HAL_I2C_Mem_Write(&hi2c1,DevAddr,aapcAddr,1,(uint8_t*)aapcData,1,1000);
HAL_I2C_Mem_Write(&hi2c1,DevAddr,dapcAddr,1,(uint8_t*)dapcData,1,1000);
HAL_I2C_Mem_Write(&hi2c1,DevAddr,aaccAddr,1,(uint8_t*)aaccData,1,1000);
HAL_I2SEx_TransmitReceive_DMA(&hi2s2,(uint16_t*)RX_BUFFER,(uint16_t*)RX_BUFFER,24);

・I2C
HAL_I2C_Mem_Writeという関数を使い、データを2進数で8桁(8ビット)ずつ、3つのデータを送ります。
 1つ目…WM8731のアドレス7桁(0011010)と0→DevAddr
 2つ目…設定先アドレス7桁と設定データ1桁
 3つ目…設定データ8桁
「0b」がついているのは2進数という意味です(「0x」は16進数)。個別の設定データの詳細はWM8731のデータシートを参照してください。※音量に関する設定はデフォルトのままなので、マイク入力への音は約5倍増幅されます。

・I2S
HAL_I2SEx_TransmitReceive_DMAという関数により、データ送信と受信を同時に行います。バッファデータサイズはテストしやすいようにとりあえず24としました。今回は受信データと送信データに同一の配列を用いているので、マイク入力への音とヘッドフォン出力からの音が同じになるはずです(※音量注意)。波形が乱れる場合は、Nucleoボード上の黒いスイッチでリセットするとうまくいく場合があります。I2Sはもう一つ利用することが可能(I2S3)なので、そちらを接続し受信(Mode Slave Receive)に設定した場合は、データ送信と受信を分けることができます。



<参考ページ>
I2Cモジュールの使い方 - 電子工作の実験室
SPI/I2C シリアル通信 - Crescent

タグ : マイコン 

STM32 Nucleoボード スイッチ割り込み


AVRマイコンでのスイッチ処理は、今までメインループの中で行っていました(→MOSリレーバイパス)。しかしながらメインループでは別の処理をしたい場合が出てくると思われるので、割り込みを使ってみます。

スイッチが押されたときに指定した割り込み処理が起きるわけですが、スイッチのチャタリングにより割り込みが何度も発生してしまうことになります。今回は対策が楽なハードウェア(抵抗とコンデンサ)によるチャタリング除去を行うことにしました。テストとしてボード上のLEDの点滅速度をスイッチにより変更します。

Nucleoボードにはすでに青いスイッチが付いており、さらにもう一つタクトスイッチを下図の通り接続します。コンデンサに直列に抵抗を入れるのはあまり見かけませんが、Nucleoボード上の青いスイッチ周辺に採用されていました。




<STM32CubeMX(5.0.0) Pinout & Configurationタブ>
右側IC画像
 14番ピン(PA0)にGPIO_EXTI0を設定
 2番ピン(PC13)がデフォルトでGPIO_EXTI13に設定済
 21番ピン(PA5)がデフォルトでGPIO_Outputに設定済

左側列のSystem Core→GPIOを開く
・中央列下側 Configuration→GPIOタブ

 PA0 GPIO mode : External Interrupt Mode with Falling edge trigger detection
 PA0 GPIO Pull-up/Pull-down : Pull up
・中央列下側 Configuration→NVICタブ

 EXTI line0 interrupt : Enabledにチェックを入れる
 EXTI line[15:10] interrupts : Enabledにチェックを入れる



<TrueSTUDIO(9.1.0)>
main.cに3箇所追加記載する

/* USER CODE BEGIN 0 */の下 変数(LED点滅間隔時間)を定義
volatile uint16_t interval = 200;

/* USER CODE BEGIN 3 */の下 LED点滅
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
HAL_Delay(interval);

/* USER CODE BEGIN 4 */の下 スイッチによりLED点滅間隔時間を変更
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_13) interval = 2 * interval;
if (GPIO_Pin == GPIO_PIN_0) interval = interval / 2;
}



<参考ページ>
割り込みを使う - DSP空挺団

(ハードウェア以外でのチャタリング対策)
割り込み処理 -第3回:チャタリング防止 - Arduinoで遊ぶブログ
マイコンにおけるチャタリング&ノイズ対策 - 味わい尽くせ!

タグ : マイコン 

管理人

管理人

自己紹介のページ
記事一覧
Twitter
GitHub

ブログ内検索
カテゴリー
タグ

自作エフェクター   レイアウト   回路図   歪み   PureData   周波数特性   波形・倍音   マイコン   RaspberryPi   エレキギター   アンプ   エフェクター自作方法   歪率   エレキベース   真空管   コーラス   ピックアップ   静音ギター   ヘッドフォンアンプ   擬似ギター出力   市販エフェクター   アコースティックギター   ブースター   コンデンサ   ソロギター   ポールピース   イコライザー   コンプレッサー   ビブラート   フェイザー   トレモロ   TAB譜   ディレイ   DIY_Layout_Creator   ワウ   オートワウ   バッファー   

最近の記事
最新コメント
Twitter
RSS
メールフォーム
当ブログに関するお問い合わせはこちらからお願いします。 ※FAQ(よくある質問)もお読みください。

お名前
メールアドレス
件名
本文

アクセスカウンター