製作モジュール一覧

ラベル TDA1543 の投稿を表示しています。 すべての投稿を表示
ラベル TDA1543 の投稿を表示しています。 すべての投稿を表示

2021年3月1日月曜日

STM32CubeIDE: I2Sをサンプリング周波数192kHzで使う

STM32F446REのI2SのFS(サンプリング周波数)は最大192kHzなので、FS=192kHzで動作させてみます。TDA1543も192kHzまで対応しているのでDACはTDA1543を使用します。ただしbit長は16bitです。

配線等ハードウェア構成は「STM32CubeIDE: I2S DACのTDA1543を使う - OPアンプでI-V変換」のものを使います。

STM32CubeIDE: Version 1.5.1
Target board: Nucleo-F446RE

MXの設定 (Clock Configuration)



ClockソースにHSEを指定します。Nucleo64の場合、ST-Linkパート上の8MHzの水晶振動子を使うことになります。デフォルトのHSIはRC発振なのでクロックの精度が上がります。

HCLKはデフォルトでは84MHzになっていますが、MAXの180MHzを入力すると、PLLやプリスケーラなどを自動的に設定してくれると思います。

MXの設定 (Pinout & Configuration)


System Core
  GPIO
    PC5: 
      GPIO mode: Output Push Pull
      User Label: CK_PERIOD
    PC6:
      GPIO mode: Output Push Pull
      User Label: CK_CPLT
    PC8:
      GPIO mode: Output Push Pull
      User Label: CK_HALF_CPLT
Multimedia
  I2S2
    Mode
      Mode: Half-Duplex Master
  Configuration
    Parameter Settings
      Generic Parameters
        Selected Audio Frequency: 192KHz
    DMA Settings
      SPI2_TX
        DMA Request Settings
          Mode: Circular
          Peripheral: Data Width: Half Word
          Memory   : Data Width: Half Word

処理のタイミングを計測するため、GPIOを何本か追加しています。


Selected Audio Frequencyで192KHzを選択すると、Real Audio Frequencyが195.312KHz、Error between Selected and Realが1.72%になります。正確に192KHzにするためにはClock源を192KHzの2^N倍にする必要があります。例えば12.288MHzの水晶振動子を外部クロックとして使うなど。

※設定時に何らかのエラーが出た場合はClock ConfigurationをResolve Clock Issueしたり、.iocファイルを一旦閉じて開き直すと改善する場合があります。

main.cにコードを追加


/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <math.h>
/* USER CODE END Includes */

浮動小数点数演算を行うため<math.h>をインクルードします。

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define PI_F	(3.141592f)
/* USER CODE END PD */

πの値を単精度で定義します。

/* USER CODE BEGIN PV */
float sampling_rate = 195312.0f;
float frequency = 1000.0f;
float phi = 0.0f;
float delta;
uint16_t tx_buffer[2] = { 0, 0 };
/* USER CODE END PV */

サンプリングレートを195.312kHzと定義しています。オーディオデータを再生するには正確に192kHzにしなければなりませんが、プログラムで波形を生成するので実際のサンプリング周波数で計算することにします。

出力波形の周波数は1kHzとしています。

phiは位相角で、サンプリング周期ごとにdeltaだけ増分します。

tx_bufferはDMA転送に使うバッファです

  /* USER CODE BEGIN 1 */
  delta = (2.0f * PI_F * frequency) / sampling_rate;
  /* USER CODE END 1 */

増分deltaを計算します。

  /* USER CODE BEGIN 2 */
  HAL_I2S_Transmit_DMA(&hi2s2, tx_buffer, 2);
  /* USER CODE END 2 */

DMA経由でI2Sを開始します。Ciruclarモードにしているので呼び出しは1回だけで勝手にメモリ→ペリフェラル間の転送が繰り返されます。

/* USER CODE BEGIN 4 */
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
	HAL_GPIO_WritePin(GPIOC, CK_HALF_CPLT_Pin, GPIO_PIN_SET);

	// Generate Sine wave
	float fv = sinf(phi);
	int16_t v = fv * 0x7fff;
	tx_buffer[0] = (uint16_t)v;

	HAL_GPIO_WritePin(GPIOC, CK_HALF_CPLT_Pin, GPIO_PIN_RESET);
}

void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	HAL_GPIO_WritePin(GPIOC, CK_CPLT_Pin, GPIO_PIN_SET);

	// Generate Sawtooth wave
	float fv = phi / PI_F;
	int16_t v = fv * 0x7fff;
	tx_buffer[1] = (uint16_t)v;

	// Advance in phase
	phi += delta;
	if (phi > PI_F) {
		phi -= 2.0f * PI_F;
		HAL_GPIO_TogglePin(GPIOC, CK_PERIOD_Pin);
	}

	HAL_GPIO_WritePin(GPIOC, CK_CPLT_Pin, GPIO_PIN_RESET);
}
/* USER CODE END 4 */

割り込みハンドラで、Lchはサイン波、Rchはノコギリ波を生成しています。

Build Setting


最適化を速度優先にします。


Project > Properties
C/C++ Build > Settings > Tool Settings > MCU GCC Compiler > Optimization

Optimization leverl: Optimize for size (-Os)

実行結果


Analog Discovery 2で出力波形とI2S信号を観測しました。

出力波形

C1:Lch C2:Rch

I2S信号


Lch(サイン波) スペクトラム(500kHzレンジ)

Lch(サイン波) スペクトラム(100kHzレンジ)

FS=48KHzの場合と比べて可聴帯域内のエイリアスは少なくなっています。

最適化フラグを-Ofastとして速度優先にしましたが、デフォルトの最適化なし-O0だと割り込み中の処理が間に合わず出力波形が乱れました。

出力波形

C1:Lch C2:Rch

I2S信号

Cpltが呼び出されず、HalfCpltが二回連続していてかなり怪しいことになっています。

FPUが搭載されているので高速に浮動小数点数演算ができますが、それほど余裕があるわけでもないようです。凝った信号処理をしようと思うとDSPを使う必要があるかも知れません。

今回は16bit長ですが、24bitや32bitにするには、浮動小数点数を整数に変換する処理が16bitが32bitかの違いなので、それほど負荷はないと思います。

STM32F4では、24(32)bit/96kHzや24(32)bit/48kHzあたりを使うか、1chのみの出力にするのが現実的でしょうか。

 

2021年2月19日金曜日

STM32CubeIDE: I2S DACのTDA1543を使う - OPアンプでI-V変換

TDA1543のデータシートに載っているようにOPアンプを使ってI-V変換を行います。STM32のプログラムは抵抗によるI-V変換の場合と同じです。サイン波とノコギリ波を出力します。

回路図 (I-V変換)

この回路は反転出力になりますが、TDA1543の電流出力が吸い込み型のためI2Sのデータと同相の電圧出力になります。

出力電圧 VO = I * R1 で、抵抗によるI-V変換の場合と同じ振幅になるように R1=2.2kΩとしました。TDA1543の出力電流のフルスケールは2.30mA(typ.)なので、出力電圧VOは

VO = I * R1 = 2.3mA * 2.2kΩ = 5.06V

となります。

今回は生の出力波形を見たいので、LPFの特性は必要ないのですがC1を入れないと発振してしまうのでC1=100pFとしました。

この回路の高域でのカットオフ周波数fcは、

fc = 1 / (2 * π * C * R) = 1 / (2 * π * 100pF * 2.2kΩ) ≒ 724kHz

となります。

R2は出力電流にかけるバイアス電流を設定するためのもので(TDA1543の機能)、この回路の場合1.8kΩにすると出力波形の中心がGND付近になります。きっちりDC成分を除去するにはトリムを入れて微調整するかACカップリングすると良いと思います。

配線


電源電圧は±5Vにしています。TDA1543のVDDの絶対定格はMin=0V Max=+9Vなので要注意。

出力波形



C1:Lch C2:Rch

振幅Vpp=5V、平均値average≒23mVとなりました。抵抗によるI-V変換の場合波形の下側がややつぶれて歪んでいましたが、OPアンプを使うとつぶれは見られません。

スペクトラム


Lch(サイン波) 500kHzレンジ

サンプリング周波数の48kHzの倍数付近にピークが現れています。

Lch(サイン波) 100kHzレンジ

サンプリング周波数以下でも、ナイキスト周波数の24kHzを中心に折返しノイズが見られます。特性の良いLPFを通すとどうなるかいずれ実験してみたいと思います。

Rch(ノコギリ波) 50kHzレンジ

ノコギリ波は基本波の整数倍の高調波で構成されますが、高調波の間のエイリアスが周波数が高くなると徐々に増えていき、サンプリング周波数の48kHz付近で共鳴しているかのようにピークが消えています。数式で求めるとどうなるんでしょうね。 

2021年2月14日日曜日

STM32CubeIDE: I2S DACのTDA1543を使う - サイン波を出力

前回と同じく抵抗でI-V変換を行います。

プログラムを変更して任意の周波数のサイン波を出力してみます。今までAVRやPSoCなどマイコンでの波形生成には計算量の少ない整数演算で済むDDSを使っていましたが、STM32F446REには単精度のFPUが搭載されているので、リアルタイムに浮動小数点数演算して波形生成してみることにします。

TDA1543のPCMデータ・フォーマット


SPIのDACの場合符号なし整数のデータフォーマットが多いですが、I2SのDACはほとんど2の補数の符号付き整数のデータフォーマットです。

16bit値の2の補数の符号付き整数は以下のようになります。

16進数 符号なし10進数 符号付き10進数
0x0000 0 0
0x0001 1 1
0x0002 2 2
: :
0x7FFD 32765 32765
0x7FFE 32766 32766
0x7FFF 32767 32767
0x8000 32768 -32768
0x8001 32769 -32767
0x8002 32770 -32766
: : :
0xFFFD 65533 -3
0xFFFE 65534 -2
0xFFFF 65535 -1

ちょっとややこしそうですが、幸いに(?)C言語のsigned intは2の補数なのでプログラムは難しくありません。

STM32CubeIDE: Version 1.5.1
Target board: Nucleo-F446RE

MXの設定






System Core
  GPIO
    PC5: 
      GPIO mode: Output Push Pull
      User Label: CK_PERIOD
    PC6:
      GPIO mode: Output Push Pull
      User Label: CK_CPLT
    PC8:
      GPIO mode: Output Push Pull
      User Label: CK_HALF_CPLT
Multimedia
  I2S2
    Mode
      Mode: Half-Duplex Master
  Configuration
    Parameter Settings
      Generic Parameters
        Selected Audio Frequency: 48KHz
    DMA Settings
      SPI2_TX
        DMA Request Settings
          Mode: Circular
          Peripheral: Data Width: Half Word
          Memory   : Data Width: Half Word

処理のタイミングを計測するため、GPIOを何本か追加しました。

配線



追加したGPIOをH/Lし、Logic Analyzerで補足します。

main.cにコードを追加

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <math.h>
/* USER CODE END Includes */

浮動小数点数演算を行うため<math.h>をインクルードします。

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define PI_F	(3.1415926f)
/* USER CODE END PD */

πの値を単精度で定義します。

/* USER CODE BEGIN PV */
uint32_t sampling_rate = 48000u;
float frequency = 1000.0f;
float phi = 0.0f;
float delta;
uint16_t tx_buffer[2] = { 0, 0 };
/* USER CODE END PV */

サンプリングレート48kHz、出力波形の周波数1kHzと定義しています。

phiは位相角で、サンプリング周期ごとにdeltaだけ増分します。

tx_bufferはDMA転送に使うバッファです。

int main(void)
{
  /* USER CODE BEGIN 1 */
  delta = (2.0f * PI_F * frequency) / sampling_rate;
  /* USER CODE END 1 */

増分deltaを計算します。

  /* USER CODE BEGIN 2 */
  HAL_I2S_Transmit_DMA(&hi2s2, tx_buffer, 2);
  /* USER CODE END 2 */

DMA経由でI2Sを開始します。Ciruclarモードにしているので呼び出しは1回だけで勝手にメモリ→ペリフェラル間の転送が繰り返されます。

/* USER CODE BEGIN 4 */
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
	HAL_GPIO_WritePin(GPIOC, CK_HALF_CPLT_Pin, GPIO_PIN_SET);

	// Generate Sine wave
	float fv = sinf(phi);
	int16_t v = fv * 0x7fff;
	tx_buffer[0] = (uint16_t)v;

	HAL_GPIO_WritePin(GPIOC, CK_HALF_CPLT_Pin, GPIO_PIN_RESET);
}

void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	HAL_GPIO_WritePin(GPIOC, CK_CPLT_Pin, GPIO_PIN_SET);

	// Generate Sawtooth wave
	float fv = phi / PI_F;
	int16_t v = fv * 0x7fff;
	tx_buffer[1] = (uint16_t)v;

	// Advance in phase
	phi += delta;
	if (phi > PI_F) {
		phi -= 2.0f * PI_F;
		HAL_GPIO_TogglePin(GPIOC, CK_PERIOD_Pin);
	}

	HAL_GPIO_WritePin(GPIOC, CK_CPLT_Pin, GPIO_PIN_RESET);
}
/* USER CODE END 4 */

割り込みハンドラで、Lchはサイン波、Rchはノコギリ波を生成しています。

// Generate Sine wave
float fv = sinf(phi);
int16_t v = fv * 0x7fff;
tx_buffer[0] = (uint16_t)v;

sinf()の返り値は-1~1なので、0x7fffを乗算するとint16_t型(2の補数の16bit符号付き整数)の最小~最大になります。tx_bufferが符号なしのuint16_t型なので、明示的にキャストしています。キャストしても2進数としては同じ値のままでメモリに格納されます。

処理にかかった時間を計測するため、ハンドラ内の最初と最後でGPIOをH/Lしています。

Analog Discovery 2で出力波形と信号を観測


TDA1543の出力波形

C1:Lch C2:Rch

ノー・オーバーサンプリングで48kHz/16bitで1kHzの波形なので、きっちりガタガタが現れています。

周波数(MeasureのFrequency)を見ると992.7kHzとなっています。STM32CubeIDEのMXではサンプリング周波数の誤差が-0.79%と表示されていて、1,000Hz * 0.79% = 7.9Hz なので仕方ないでしょう。実際の値として見ると少し大きい感じがしますね。

計算処理時間を計測



1ch分転送後、割り込みハンドラに処理が移るまで結構タイムラグがあるようです。

処理にかかった時間は、Quick Measure機能で信号がHighになっている時間を調べると

HalfCplt: 3.51us (284.9kHz)
Cplt: 1.56us (641.0kHz)

でした。Clockが84MHz、DebugプロファイルでBuildした場合の計測なのでもう少し速くできると思います。

2021年2月11日木曜日

STM32CubeIDE: I2S DACのTDA1543を使う - 抵抗でI-V変換

TDA1543はDIP8、5V駆動可、オーバーサンプリングなしという実験に使いやすいI2S DACです。SPI DACだと出回っているものはせいぜい12bit精度ですが、TDA1543はオーディオ用なので16bit精度、サンプリングレートは192kHzまで対応しています。

TDA1543はTDA1543Aというフォーマットが異なるものがあるので要注意です。


前回の「STM32CubeIDE: I2SをDMAで使う」のプログラムをそのまま使ってみます。

配線図

TDA1543は電流出力型です。電圧として取り出すにはI-V変換を行う必要がありますが、簡単にオームの法則(V=IR)を使って抵抗1本で行いました。

データシートを見ると、

  • 電源電圧VDD MAX.=8.0V
  • フルスケール出力電流IFS=2.30mA(typ)
  • 出力電圧コンプライアンスVOC(DC) MIN.=1.8V MAX.=VDD-1.2V

となっています。VDD=8V駆動するとI-V変換抵抗RLは

RL = (VDD - 1.2)[V] / IFS[mA] = (8V - 1.2V) / 2.30mA = 2.96kΩ

となります。RLが大きいほど出力電圧が高くなるので余裕をもって2.2kΩとしました。

TDA1543のVref(7ピン)で出力のバイアス電流を設定できます。

接続する抵抗の値によって出力電流にかかるバイアスの量が変わります。I-V変換後の電圧がコンプライアンス電圧に引っかからないように、抵抗値を設定する必要があります。コンプライアンス電圧を超えると出力波形がクリップしてしまいます。

カットアンドトライの結果、この回路の場合1.5kΩが良いようです。

出力波形とI2S信号


VDD=8V

プログラムではLchをインクリメント、Rchをデクリメントしています。LogicのI2Sの値を見てもそうなっています。

ところが、Scopeの波形を見るとLch(C1:黄)が右肩下がり、Rch(C2:青)が右肩上がりになっており、プログラムとは逆の動作になっています。

これはTDA1543の電流出力が吸い込み(Sink)型のためです。この回路では波形が反転して出力されることになります。

TDA1543のデータシートのブロック図(一部)

TDA1543のデータシートのブロック図を見ると、OPアンプを使った反転型のI-V変換回路が使われています。こちらだと反転されずに電圧が出力されることになります。

また図のようにR=1.2kΩ、C=3.3nFだと、カットオフ周波数fcが

fc = 1 / (2 * π * C * R) = 1 / (2 * π * 3.3nF * 1.2kΩ) = 40.1kHz

の1次LPFの特性を持ちます。

コンプライアンス電圧


同じ回路で電源電圧VDD=5Vにするとコンプライアンス電圧に引っかかって出力がクリップします。

VDD=5V

VDD=5Vの場合、コンプライアンス電圧VOC = (VDD - 1.2) = 5 - 1.2 = 3.8Vとなりますが、実際にはもう少し高い電圧まで出力されています。

2019年5月8日水曜日

TDA1543とTDA1543Aの違い(I2SとJapanese Format)

I2SのDACとして実験するのにTDA1543は便利です。ブレッドボードで使いやすいDIPパッケージで、電源も5Vで使えます。

I2Sの実験用にAliExpressで安く出ていたので買ってみたのですが、「TDA1543」ではなく、A付きの「TDA1543A」でした。

最初パチもんをつかまされたかと思いました。「A」がつくかつかないかでデータ転送のフォーマットが違うそうです。

TDA1543

TDA1543A

TDA1543はPSoC 5LPで使ったことがあるので検証しました。

検証用のPSoC 5LPのプロジェクト
https://github.com/ryood/TDA1543A

ブレッドボード配線図

出力波形


TDA1543

ch1:AOR ch2:AOL

サイン波の波形が歪んでいるのは抵抗1本でI-V変換しているためです。ガタガタは16bit/48kHzのD-A変換のためです。

TDA1543A

ch1:AOR ch2:AOL

無印と「A」付きは、ピンはコンパチですが、差し替えると波形がグチャグチャです。

データーシートを見ると

TDA1543 (I2S)

TDA1543A (Japanese format)

比べるとWSがL/Hするのと、DATAの頭と尻尾のタイミングが違っています。

自前でハードウェアを作るとか、コーディングするとかしないと「A」付きは難しそうです。

2016年1月30日土曜日

TDA1543のI-V変換にオペアンプを使ってみる。

LTSpiceでのシミュレーションだけ。

回路図

TDA1543は「full scale output current」が2.30mA(typ.)となっているので
2.30mA × 820Ω ≒ 1.89V
ぐらいにしてみた。

OPAMPは入力インピーダンスを高くしたいのでFET入力のOPA2134にして電源はとりあえず5Vを分圧。

帰還に入れているCを100pF、1000pF、0.01uFにしてシミュレーションしてみた。

過渡解析

出力振幅は1.8Vp-p弱になっている。

AC解析

青色で見にくいですが、1000pFぐらいだとカットオフ周波数が200~300kHz前後にくるようだ。

ファンクションジェネレータとして使うのできれいな正弦波を出力したい。もう何段かLPFを入れてTDA1543+アナログフィルタでガタガタをとるのはしんどそうかな。

2016年1月24日日曜日

PSoC 5LPとオーディオ用DACを使ったファンクションジェネレータの構想

Github:
https://github.com/ryood/TDA1543/tree/master/PSoC/I2S_Test/PrototypingKit_DDS.cydsn

前回、DDSで波形を生成したとき10kHzのサイン波がブレブレだったのはDMAの転送元のバッファの書き換えがI2Sの送信に追いついていないのが原因だったようだ。

バッファを2ch分でなく同一のものを参照するようにしたらブレはなくなった。

10kHz

100kHz

129.8kHz

LPFを入れていないのでガタガタはあいかわらずですが。

I2Sのクロックは9MHzにしたのでサンプリングレートは9MHz/32(bit)=281.25kHzだが、PSoC 5LPの内蔵クロックの誤差を測定してSampling Rateを279658Hzに設定した。

129.8kHzの測定はこのサンプリングレートの1/2の周波数をDDSで出力したもので矩形波になっている。

このままTDA1543でLPFを入れるか、PCM5102AにするかわからないがDDSで波形を生成する方向でやってみようと思う。

入力インターフェイス

ロータリーエンコーダーで周波数を選択するようにコードを書いたが、100kHz程度まで出力できて1Hz単位で周波数を設定するのは結構しんどいのでキーパッドとロータリーエンコーダーを組み合わせて選択することを考えてみた。


メモ:

192kHz/32bitでI2Sのクロックは12.288MHz。12MHzにするとCPUの駆動クロックは6倍で72MHz、7倍で84MHz(PSoC 5LP Prototyping Kitは80MHzでなので仕様外)。

2016年1月22日金曜日

PSoC 5LPでI2Sのテスト TDA1543編 DMAを使ってDDSで波形を出力

I2SコンポーネントにDMA転送しつつ波形テーブルを更新してみた。

GitHub
https://github.com/ryood/TDA1543/tree/master/PSoC/I2S_Test/PrototypingKit_DMA.cydsn

PSoC CreatorのTopDesign

DMAコンポーネントのデーターシートを読むと「Ping Pong」や「Round Robin」という技が載っていたが、(読むのがめんどくさいので)まずは簡単にDMA転送が終わったら割り込みを発生させてその隙に波形テーブルを更新できないかテストしてみた。

I2Sコンポーネントのデーターシートを読むとデータを保持するFIFOは各チャンネル4ByteあるらしいのでI2Sの転送スピードより速くメモリーを更新できれば間に合いそうだ。

また、今回はL/Rチャンネルをインターリーブではなく独立してDMAからデータを転送する設定にした。

↑Data interleaving:のところ

DMA転送完了の割込み(nrq)を使うにはDMA Wizardで「Enable nrq」をチェックする必要があるようだ。


以下の様なコードが生成される。

 /* DMA Configuration for DMA_0 */  
 DMA_0_Chan = DMA_0_DmaInitialize(DMA_0_BYTES_PER_BURST, DMA_0_REQUEST_PER_BURST,   
     HI16(DMA_0_SRC_BASE), HI16(DMA_0_DST_BASE));  
 DMA_0_TD[0] = CyDmaTdAllocate();  
 CyDmaTdSetConfiguration(DMA_0_TD[0], TABLE_LENGTH*2, DMA_0_TD[0], DMA_0__TD_TERMOUT_EN | TD_INC_SRC_ADR);  
 CyDmaTdSetAddress(DMA_0_TD[0], LO16((uint32)waveTable0), LO16((uint32)I2S_1_TX_CH0_F0_PTR));  
 CyDmaChSetInitialTd(DMA_0_Chan, DMA_0_TD[0]);  
 CyDmaChEnable(DMA_0_Chan, 1);  

nrqで割込みを発生させて以下の様なコードでサイン波とノコギリ波を交互に出力するようにした。

 int8 waveTable0[TABLE_LENGTH*2];  
 int8 waveTable1[TABLE_LENGTH*2];  
   
 const uint8 sineTable8[TABLE_LENGTH] =   
 {  
     128, 134, 140, 147, 153, 159, 165, 171,  
     177, 183, 188, 194, 199, 204, 209, 214,  
     218, 223, 227, 231, 234, 238, 241, 244,  
     246, 248, 250, 252, 253, 254, 255, 255,  
     255, 255, 255, 254, 253, 252, 250, 248,  
     246, 244, 241, 238, 234, 231, 227, 223,  
     218, 214, 209, 204, 199, 194, 188, 183,   
     177, 171, 165, 159, 153, 147, 140, 134,   
     128, 122, 115, 109, 103, 97, 91, 85,  
      79, 73, 68, 62, 57, 52, 47, 42,  
      37, 33, 29, 25, 22, 18, 15, 12,  
      10,  7,  6,  4,  2,  1,  1,  0,  
      0,  0,  1,  1,  2,  4,  6,  7,  
      10, 12, 15, 18, 22, 25, 29, 33,  
      37, 42, 47, 52, 57, 62, 68, 73,  
      79, 85, 91, 97, 103, 109, 115, 122  
 };       
   
 void genSineTable(void* buff)  
 {  
   int i;  
   
   // 符号付き16bit sineTableの生成  
   for (i = 0; i < TABLE_LENGTH; i++) {  
     ((int8 *)buff)[i*2]  = (int)sineTable8[i] - 128;  
     ((int8 *)buff)[i*2+1] = 0;  
   }  
 }  
   
 void genSawTable(void* buff)  
 {  
   int i;  
     
   // 符号付き16bit sawTableの生成  
   for (i = 0; i < TABLE_LENGTH; i++) {  
     ((int8 *)buff)[i*2]  = i * 2 - 128;  
     ((int8 *)buff)[i*2+1] = 0;  
   }  
 }  

TDA1543の出力

ch1はノコギリ波、ch2はサイン波とノコギリ波をテーブルを更新して交互に出力させている。想定通りの波形が出力された。

波形テーブル更新の前後でH/Lさせたもの(波形テーブルの書き換え時間)を捕捉


ch1:波形テーブルの書き換え時間 ch2:TDA1543の出力

拡大

ch1とch2の立ち上がりがずれているのは、I2SコンポーネントのFIFOにたまっている分だと思う。ch2の立ち上がりで実際にI2Sのデータが送信されはじめていると思うので、テーブルの更新の完了(ch2の立下り)は間に合っていないみたいだがDMA転送はCPUと独立しているのでなんとかなっている感じだ。

テーブル長が128なのでこれを32ぐらいに減らせば安心かな?

DDSで出力

Github
https://github.com/ryood/TDA1543/tree/master/PSoC/I2S_Test/PrototypingKit_DDS.cydsn

DDSで波形を生成するようにしてみた。

符号付16bitでテーブルを作成したので、これをI2Sのバイトストリームの順序に合うようにバイト順を入れ替えてバッファに転送した。
 void generateWave_0()  
 {  
   int i, index;  
   uint8* p8;  
    
   // 波形をバッファに転送  
   for (i = 0; i < BUFFER_SIZE; i+=2) {  
     phaseRegister_0 += tuningWord_0;  
     index = phaseRegister_0 >> 22;  
      
     p8 = (uint8 *)(sineTable + index);  
     waveBuffer_0[i]  = *(p8 + 1);  
     waveBuffer_0[i+1] = *p8;  
   }  
 }  

TDA1543はデーターシートを見るとビットレート9.2Mbits/sまで大丈夫そうなので、I2Sコンポーネントの入力クロックを12MHz(I2Sのビットレートは6MHzになる)に設定して、1kHzと10kHzのサイン波をL/Rに分けて出力してみた。


ch1:LOUT ch2:ROUT

だいたい10kHzと1kHzのサイン波が出力されている。LPFをいれていないので10kHzの方はガタガタでブレブレ。

WS(ワードセレクト)

ch1:LOUT ch2:WS

WSの周波数がサンプリング周波数になる。

I2Sコンポーネントに12MHzのクロックを入れているので
12MHz / 16(bit) / 2(ch) / 2 = 187.5kHz
ちょっとずれているが、まあまあ近い値だ。

PSoC 5LP Prototyping Kitの内蔵クロックの誤差かな?

メモ:

サンプリングレートが187.5kHz程度でもDDSだと10kHzのサイン波形はジッターがかなり出る。周波数設定時に浮動小数点演算で1周期分の波形を生成するという手もありそう。

波形テーブルにSRAMを64kBのうち32kB使うとして最大16kサンプル
192kHz / 16k = 10.125Hz
工夫無しで10Hzぐらいから出力可能。計算時間はかなりかかりそうだけど。

元の波形テーブルを1024でなくもっと増やす?

う~ん。